Patterns
Multimedia block
View in FigmaA 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/multimediaBlockPreview
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.
<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
| Prop | Type | Default | Description |
|---|---|---|---|
| media | MediaContent[] | — | Carousel slides. Each item is discriminated by `type`: `image` (default) or `video`. |
| label | string | — | Accessible 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. |
| textTile | TextTileDefaultProps | — | Caption 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. |
| flip | boolean | false | Places the carousel below the text panel instead of above it. |
| escape hatch | compose lower layer | — | For custom captions, handlers, or framework image elements, compose MediaCarousel and render helpers directly. |


