Sections

Services list

View in Figma

Two-column services section where hovering a link previews it over the opposite column — flat link, media and text-tile records.

Install

npx shadcn@latest add @alix/servicesList

Preview

Usage

import ServicesList, { type ServicesListProps } from "@/components/servicesList";

const fromCMS: ServicesListProps = await getServicesList();

<ServicesList {...fromCMS} />

<ServicesList
  leftColumn={{
    listTitle: "Industries",
    items: [
      {
        text: "Financial services",
        href: "/industries/financial-services",
        media: { type: "image", src: "/media/grey-arc.svg", alt: "" },
        textTile: {
          variant: "titleAndText",
          bgColor: "primary",
          title: "Financial services",
          description: "Move quickly through complex market events.",
          showButtonOne: true,
          buttonOne: { href: "/industries/financial-services", text: "Explore", iconRight: "arrowRight" },
        },
      },
    ],
  }}
  rightColumn={{ listTitle: "Capabilities", items: [] }}
/>

Examples

01

Large-text previews on a light surface

Each preview tile can be a single statement instead of a titled block — the largeText variant on a sand surface reads as a perspective, not a link.

<ServicesList
  leftColumn={{
    listTitle: "Perspectives",
    items: [
      {
        text: "The first hundred days",
        href: "/insights/first-hundred-days",
        media: { src: "/media/grey-arc.svg", alt: "" },
        textTile: {
          variant: "largeText",
          bgColor: "sand",
          text: "The decisions that stabilize a business are made in the first weeks.",
        },
      },
    ],
  }}
  rightColumn={{ listTitle: "Capabilities", items: capabilities }}
/>
02

Video hover previews

Flip each preview's media discriminant to video for muted, looping footage over the opposite column, on a secondary surface tile.

<ServicesList
  leftColumn={{
    listTitle: "Industries",
    items: [
      {
        text: "Financial services",
        href: "/industries/financial-services",
        media: {
          type: "video",
          src: "/media/financial-loop.mp4",
          poster: "/media/grey-arc.svg",
          autoPlay: true,
          muted: true,
          loop: true,
        },
        textTile: {
          variant: "titleAndText",
          bgColor: "secondary",
          title: "Financial services",
          description: "Move quickly through complex market events.",
          showButtonOne: true,
          buttonOne: { href: "/industries/financial-services", text: "Explore", iconRight: "arrowRight" },
        },
      },
    ],
  }}
  rightColumn={{ listTitle: "Capabilities", items: capabilities }}
/>

When to use it

Use it for

  • A two-column services section where hovering a link previews it over the opposite column — the signature Services List move.
  • Pairing Industries with Capabilities so a visitor answers 'what's this?' without navigating away.
  • CMS-fed lists where each item carries its own preview media and text tile, spread straight from the query.

Reach for something else

  • A complete, alphabetized directory of everything — use ServicesIndex for coverage without the hover preview.
  • A plain rail of links with no preview — use List directly.
  • A different interaction such as click-to-pin or a touch carousel — compose List, Grid, renderMedia, and renderTextTile directly.

Props

PropTypeDefaultDescription
leftColumn / rightColumn{ listTitle: string; items: ServiceListEntry[] }The two CMS-provided lists. Each item controls its hover preview.
ServiceListEntry{ text: ReactNode; href: string; current?: boolean; media: MediaContent; textTile: TextTileContent }A List link item plus preview media and text-tile content for the opposite-column overlay.
media.type"image" | "video""image"Discriminant for each hover-preview media record.
textTile.variant"titleAndText" | "largeText" | "split"Shared text-tile discriminant. `titleAndText` uses `title`/`description`; `largeText` uses `text`; `split` includes its own media.
textTile.bgColor"default" | "transparent" | "primary" | "secondary" | "sand" | "greyGradient" | "goldGradient"Surface treatment for each preview tile.
buttonOne / buttonTwo{ href: string; text: string; openInNewTab?: boolean; iconLeft?: ActionIcon; iconRight?: ActionIcon }Optional action data. Icon names are "arrowRight" | "arrowUpRight" | "chevronRight" | "externalLink" | "download".
escape hatchcompose lower layerFor server-driven previews or a different interaction model, compose List, Grid, renderMedia, and renderTextTile directly.

See also