Patterns

Multimedia block

View in Figma

A media carousel paired with a caption tile — flat media data, a text-tile caption, and surface enums, with every layout decision pre-made.

Install

npx shadcn@latest add @alix/multimediaBlock

Preview

Operational improvement case studies

Browse recent work across liquidity, margin, and transaction readiness.

Usage

import { MultimediaBlock, type MultimediaBlockProps } from "@/components/multimediaBlock";

const fromCMS: MultimediaBlockProps = await getBlock("case-study-carousel");

<MultimediaBlock {...fromCMS} />

<MultimediaBlock
  label="Operational improvement case studies"
  bgColor="default"
  ratio="16/9"
  media={[
    { type: "image", src: "/article-placeholder.svg", alt: "" },
    { type: "video", src: "/media/turnaround-loop.mp4", poster: "/media/grey-arc.svg", muted: true },
  ]}
  textTile={{
    bgColor: "transparent", // the block forces the caption transparent either way
    sectionTitle: {
      title: "Operational improvement case studies",
      description: "Browse recent work across liquidity, margin, and transaction readiness.",
      align: "start",
    },
    buttonGroup: {
      primaryAction: {
        children: "View insights",
        rightIcon: <ArrowRight />,
        render: <a href="/insights" />,
      },
    },
  }}
/>

Examples

01

Video slide on the navy surface

A video item (type: "video") sits beside images in the same carousel; the primary surface shows through the transparent caption tile.

Turnaround delivery in motion

How senior teams stand up a cash office and hold the operating cadence week to week.
<MultimediaBlock
  label="Turnaround delivery in motion"
  bgColor="primary"
  ratio="16/9"
  media={[
    { type: "image", src: "/brand/construction.webp", alt: "" },
    { type: "video", src: "/media/turnaround-loop.mp4", poster: "/brand/ship.webp", muted: true, loop: true },
  ]}
  textTile={{
    bgColor: "transparent",
    sectionTitle: {
      title: "Turnaround delivery in motion",
      description: "How senior teams stand up a cash office and hold the operating cadence week to week.",
      align: "start",
    },
    buttonGroup: {
      primaryAction: {
        children: "View the engagement",
        rightIcon: <ArrowRight />,
        render: <a href="/insights/turnaround-delivery" />,
      },
    },
  }}
/>
02

Large statement caption, flipped below

Switch the caption's section title to the largeText variant and flip the carousel under it when the words should lead the media, here on a sand band.

Evidence from the room where the operating decisions were made.
<MultimediaBlock
  label="Portfolio operating review"
  bgColor="sand"
  ratio="4/3"
  flip
  media={[
    { type: "image", src: "/brand/editorial-1.jpg", alt: "" },
    { type: "image", src: "/brand/editorial-2.jpg", alt: "" },
  ]}
  textTile={{
    bgColor: "transparent",
    sectionTitle: {
      variant: "largeText",
      description: "Evidence from the room where the operating decisions were made.",
      align: "start",
    },
    buttonGroup: {
      primaryAction: {
        children: "Read the review",
        rightIcon: <ArrowRight />,
        render: <a href="/insights/operating-review" />,
      },
    },
  }}
/>
03

Two actions on a grey gradient

Both button-group actions give a primary and secondary path; the grey-gradient surface reads behind the controls.

Inside a client value workshop

Two days of structured challenge that turn diligence findings into a first-hundred-days plan.
<MultimediaBlock
  label="Client workshop gallery"
  bgColor="greyGradient"
  ratio="16/9"
  media={[
    { type: "image", src: "/brand/people.webp", alt: "" },
    { type: "image", src: "/brand/portrait-1.jpg", alt: "" },
  ]}
  textTile={{
    bgColor: "transparent",
    sectionTitle: {
      title: "Inside a client value workshop",
      description: "Two days of structured challenge that turn diligence findings into a first-hundred-days plan.",
      align: "start",
    },
    buttonGroup: {
      primaryAction: {
        children: "See the approach",
        rightIcon: <ArrowRight />,
        render: <a href="/insights/value-workshop" />,
      },
      secondaryAction: {
        children: "Talk to the team",
        render: <a href="/contact" />,
      },
    },
  }}
/>

When to use it

Use it for

  • A CMS-driven case-study or product carousel where every layout and surface choice is locked.
  • Pairing a set of images or a decorative video with a caption tile a content team fills in.
  • Switching bgColor to sit the section on a navy or sand band without touching the media data.

Reach for something else

  • A slot-composed carousel with a custom caption node or click handlers — use MediaCarousel instead.
  • A single framed image or banner with copy — use TextAndMediaBlock instead.
  • Narrative video needing controls and captions — drop to MediaCarousel and compose your own video.

Props

PropTypeDefaultDescription
mediaMediaContent[]Carousel slides. Each item is discriminated by `type`: `image` (default) or `video`.
labelstringAccessible name for the carousel region.
ratio"1/1" | "4/3" | "3/4" | "5/4" | "4/5" | "3/2" | "2/3" | "16/10" | "16/9" | "9/16" | "3/1" | "21/9""16/9"Aspect ratio for the carousel frame.
textTileTextTileDefaultPropsCaption tile data — `sectionTitle` (title/description/align, or its largeText variant for one bold statement) plus `buttonGroup`. The block forces the tile's surface transparent.
bgColor"default" | "transparent" | "primary" | "secondary" | "sand" | "greyGradient" | "goldGradient""default"Surface behind the controls and caption. The nested text tile is forced transparent.
textTile.buttonGroup{ primaryAction?; secondaryAction? }Up to two actions as Button props minus `variant` — the tile maps the variant to its surface. Use `render: <a href />` for a link-shaped action.
flipbooleanfalsePlaces the carousel below the text panel instead of above it.
escape hatchcompose lower layerFor custom captions, handlers, or framework image elements, compose MediaCarousel and render helpers directly.

See also