Sections

Text and media block

View in Figma

The workhorse text-and-media section — a text tile beside an image or video frame, or a split tile carrying its own image, with surface enums and ratios.

Install

npx shadcn@latest add @alix/textAndMediaBlock

Preview

Performance improvement when pace matters

Senior teams connect diagnostics, cash, and operational delivery into a practical path forward.

Usage

import { TextAndMedia, type TextAndMediaProps } from "@/components/textAndMediaBlock";

const fromCMS: TextAndMediaProps = await getBlock("performance-intro");

<TextAndMedia {...fromCMS} />

<TextAndMedia
  textTileVariant="titleAndText"
  textTile={{
    bgColor: "primary",
    sectionTitle: {
      title: "Performance improvement when pace matters",
      description: "Senior teams connect diagnostics, cash, and operational delivery.",
      align: "start",
    },
    buttonGroup: {
      primaryAction: {
        children: "Explore capability",
        rightIcon: <ArrowRight />,
        render: <a href="/services/performance-improvement" />,
      },
    },
  }}
  media={{ type: "video", src: "https://samplelib.com/lib/preview/mp4/sample-5s.mp4", poster: "/article-placeholder.svg", muted: true }}
  ratio="4/3"
  minHeight="md"
/>

Examples

01

Media leads, on a light surface

Set reverse and the media takes the leading column on desktop; a sand surface keeps the copy quiet beside it.

A senior team, close to the decision

The people who scope the work stay through delivery, so nothing is lost in the handover.
<TextAndMedia
  textTileVariant="titleAndText"
  textTile={{
    bgColor: "sand",
    sectionTitle: {
      title: "A senior team, close to the decision",
      description: "The people who scope the work stay through delivery.",
      align: "start",
    },
    buttonGroup: {
      primaryAction: {
        children: "How we work",
        rightIcon: <ArrowRight />,
        render: <a href="/about/how-we-work" />,
      },
    },
  }}
  media={{ type: "image", src: "/media/grey-arc.svg", alt: "" }}
  ratio="16/9"
  reverse
  minHeight="md"
/>
02

A single large statement

The section title's largeText variant drops the title/description pair for one heading-sized statement — here on a grey-gradient surface.

Clarity, pace, and practical execution for situations where the outcome matters.
<TextAndMedia
  textTileVariant="titleAndText"
  textTile={{
    bgColor: "greyGradient",
    sectionTitle: {
      variant: "largeText",
      description: "Clarity, pace, and practical execution when the outcome matters.",
      align: "start",
    },
    buttonGroup: {},
  }}
  media={{ type: "image", src: "/article-placeholder.svg", alt: "" }}
  ratio="4/3"
  minHeight="md"
/>
03

Split: title, image, and body

The split variant carries its own image beside the title, then a second tile takes the description and actions — the section fills both columns from one text tile.

Two ways we engage

A senior-led diagnostic first, then a delivery team that stays through implementation.
<TextAndMedia
  textTileVariant="split"
  textTile={{
    bgColor: "secondary",
    sectionTitle: {
      title: "Two ways we engage",
      description: "A senior-led diagnostic first, then a team that stays through implementation.",
      align: "start",
    },
    buttonGroup: {
      primaryAction: {
        children: "How we work",
        rightIcon: <ArrowRight />,
        render: <a href="/about/how-we-work" />,
      },
    },
    // split carries its own image — there is no sibling media column
    media: { type: "image", src: "/article-placeholder.svg", alt: "" },
  }}
  minHeight="md"
/>
04

Video instead of a still

Flip the sibling media discriminant to video for a muted, looping clip in the frame — a poster covers it until playback starts.

Performance improvement when pace matters

Senior teams connect diagnostics, cash, and operational delivery into a practical path forward.
<TextAndMedia
  textTileVariant="titleAndText"
  textTile={{
    bgColor: "primary",
    sectionTitle: {
      title: "Performance improvement when pace matters",
      description: "Senior teams connect diagnostics, cash, and operational delivery.",
      align: "start",
    },
    buttonGroup: {
      primaryAction: {
        children: "Explore capability",
        rightIcon: <ArrowRight />,
        render: <a href="/services/performance-improvement" />,
      },
    },
  }}
  media={{ type: "video", src: "https://samplelib.com/lib/preview/mp4/sample-5s.mp4", poster: "/article-placeholder.svg", autoPlay: true, muted: true, loop: true }}
  ratio="16/9"
  minHeight="md"
/>

When to use it

Use it for

  • The workhorse content section — a text tile beside an image or video, across services, insights, and about pages.
  • Sections where the surface, text content, media, and ratio all vary per record.
  • Leading with the media by setting reverse, or with a single statement via the section title's largeText variant.

Reach for something else

  • Two text columns with no media — use TextAndTextBlock.
  • A full-width media strip with no text — use MediaBannerBlock.
  • next/image, a custom element, or bespoke layout — compose Grid with TextTileDefault and renderMedia directly.

Props

PropTypeDefaultDescription
textTileVariant"titleAndText" | "split"Layout discriminant. `titleAndText` puts the tile beside a framed media column; `split` composes a title-beside-image tile plus a body tile from the same textTile.
textTileTextTileDefaultPropsThe text tile — bgColor, sectionTitle (title/description/align, largeText variant), and buttonGroup. The split variant adds its own `media` here.
textTile.buttonGroup{ primaryAction?; secondaryAction? }Up to two actions as Button props minus `variant` — the tile maps the variant to its surface.
media.type"image" | "video""image"Sibling media discriminant (titleAndText only). The `split` variant uses `textTile.media` instead.
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 sibling media frame.
reversebooleanfalseSwaps the desktop column order; mobile keeps source order.
minHeight"none" | "md" | "lg" | "xl" | "2xl""lg"Minimum section height from the shared scale.
idstringOptional anchor id for in-page navigation.
escape hatchcompose lower layerFor next/image, custom elements, or bespoke layout, compose Grid plus TextTileDefault and `renderMedia` directly.

See also