Sections
Services list
View in FigmaTwo-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/servicesListPreview
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.
Perspectives
<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
| Prop | Type | Default | Description |
|---|---|---|---|
| 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 hatch | compose lower layer | — | For server-driven previews or a different interaction model, compose List, Grid, renderMedia, and renderTextTile directly. |