Sections
Text and text
Two-column text section of paired text tiles, with surface enums and min-height tokens. Each column is one TextTile record — a surface, a SectionTitle's content, and a buttonGroup — so a CMS query spreads straight in.
Install
npx shadcn@latest add @alix/textAndTextPreview
How we work
Senior operators and financial experts stay close to the decision, the data, and the implementation plan.
Clarity, pace, and practical execution for situations where outcomes matter.
Usage
import { TextAndText, type TextAndTextProps } from "@/components/textAndText";
const fromCMS: TextAndTextProps = await getBlock("approach-pair");
<TextAndText {...fromCMS} />
<TextAndText
minHeight="md"
contentLeft={{
bgColor: "primary",
sectionTitle: {
title: "How we work",
description: "Senior operators stay close to the decision and the data.",
align: "start",
},
buttonGroup: {
primaryAction: {
children: "Our approach",
rightIcon: <ArrowRight />,
render: <a href="/about/how-we-work" />,
},
},
}}
contentRight={{
bgColor: "greyGradient",
sectionTitle: {
variant: "largeText",
description: "Clarity, pace, and practical execution when outcomes matter.",
align: "start",
},
buttonGroup: {},
}}
/>Examples
01
Two titled columns
Both columns carry a titled SectionTitle on different surfaces — a sand tile beside a secondary one, each with its own action.
What we do
Turnaround, performance, transactions, and disputes — delivered by senior teams close to the decision.
How we engage
A senior-led diagnostic, then a delivery team that stays through implementation.
<TextAndText
minHeight="md"
contentLeft={{
bgColor: "sand",
sectionTitle: {
title: "What we do",
description: "Turnaround, performance, transactions, and disputes.",
align: "start",
},
buttonGroup: {
primaryAction: {
children: "Our services",
rightIcon: <ArrowRight />,
render: <a href="/services" />,
},
},
}}
contentRight={{
bgColor: "secondary",
sectionTitle: {
title: "How we engage",
description: "A senior-led diagnostic, then a team that stays through delivery.",
align: "start",
},
buttonGroup: {
primaryAction: {
children: "How we work",
rightIcon: <ArrowRight />,
render: <a href="/about/how-we-work" />,
},
},
}}
/>02
A primary and secondary action
One tile can pair two actions — the secondary path sits beside the primary, and the variants stay the block's per-surface opinion.
Talk to the team
Start a conversation about a situation you're facing, or arrange an introductory call.
Situations move quickly — we're set up to move with them.
<TextAndText
minHeight="md"
contentLeft={{
bgColor: "primary",
sectionTitle: {
title: "Talk to the team",
description: "Start a conversation, or arrange an introductory call.",
align: "start",
},
buttonGroup: {
primaryAction: {
children: "Contact us",
rightIcon: <ArrowRight />,
render: <a href="/contact" />,
},
secondaryAction: {
children: "Schedule a call",
render: <a href="/contact/call" />,
},
},
}}
contentRight={{
bgColor: "greyGradient",
sectionTitle: {
variant: "largeText",
description: "Situations move quickly — we're set up to move with them.",
align: "start",
},
buttonGroup: {},
}}
/>03
Reverse the emphasis
Set reverse to lead with the statement on desktop while the source order stays intact on mobile.
The detail
Diagnostics, cash management, and operational delivery, connected into one plan.
Practical execution for situations where the outcome matters.
<TextAndText
minHeight="md"
reverse
contentLeft={{
bgColor: "sand",
sectionTitle: {
title: "The detail",
description: "Diagnostics, cash management, and operational delivery in one plan.",
align: "start",
},
buttonGroup: {},
}}
contentRight={{
bgColor: "primary",
sectionTitle: {
variant: "largeText",
description: "Practical execution for situations where the outcome matters.",
align: "start",
},
buttonGroup: {},
}}
/>When to use it
Use it for
- Two text tiles side by side — a titled section beside a single statement, or two titled blocks on paired surfaces.
- Contrasting a claim with its detail, each on its own surface, without any media.
- CMS-fed sections where both columns' surface, content, and actions vary per record.
Reach for something else
- A text tile beside an image or video — use TextAndMedia.
- A single tile on its own — use TextTileDefault directly.
- A bespoke button variant or a custom element inside a column — compose Grid and the textTile primitives directly.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| contentLeft / contentRight | TextTileDefaultProps | — | The two independent text-tile records rendered side by side — each a surface (bgColor), a sectionTitle, and a buttonGroup. |
| sectionTitle | SectionTitleProps | — | Each tile's content — tagline, title, description, align, and headingSize/headingTag. Set variant: "largeText" for the single bold statement (description only, sized up). |
| bgColor | "default" | "transparent" | "primary" | "secondary" | "sand" | "greyGradient" | "goldGradient" | — | Surface treatment for each text tile. |
| buttonGroup | TileButtonGroupProps | — | Up to two actions per tile — primaryAction / secondaryAction as Button props minus variant: the tile picks the variant for the surface, so a record can't misconfigure it. |
| reverse | boolean | false | Swaps the desktop column order; mobile keeps source order. |
| minHeight | "none" | "md" | "lg" | "xl" | "2xl" | "lg" | Minimum section height from the shared scale. |
| id | string | — | Optional anchor id for in-page navigation. |
| escape hatch | compose lower layer | — | For custom elements, handlers, or a bespoke button variant, compose Grid and TextTileDefault directly. |