Content block
Fixed-slot Block-layer unit — title, subtitle, description, and actions, each independently omittable.
ContentBlock is a fixed-slot Block-layer unit for named content with a known shape: title / subtitle / description / actions, each independently omittable. It owns the vertical rhythm BETWEEN its own slots — a tight gap ties title to subtitle, a medium gap separates the last text slot from description, and the same medium gap leads into actions. See ADR-023.
ContentBlock never owns layout. Column count and orientation come from a Layout primitive (Stack, Grid) or a Container (Card) wrapped around it.
Use it for
- A card's text content — feature copy, testimonial framing, pricing tile copy
- A CTA's heading, supporting line, and button
- Any named content unit with a known title/subtitle/description/actions shape
For open-ended, already-sanitized rich text with no fixed slot shape, use Prose instead.
Import
import { ContentBlock } from '@brikdesigns/bds';Variants
All slots
<ContentBlock
title="Membership Plans"
subtitle="Choose the plan that fits your practice"
description="Every plan includes unlimited support and quarterly reviews."
actions={<Button variant="primary">View Plans</Button>}
/>Omitted slots
Any slot can be dropped independently — the rhythm collapses to whatever slots remain, with no leftover spacing from an omitted one.
<ContentBlock
title="First Impressions"
description="75% of website credibility comes from design."
/>Title element level
title renders as titleAs (default h3 — the common case nests inside a Card). Pick the element by outline position, not by convenience.
// Direct outline sibling of the page's <h1>
<ContentBlock titleAs="h2" title="Membership Plans" />
// Decorative unit repeated in a grid — not an outline node
<ContentBlock titleAs="div" title="Feature name" />See HTML Semantics — Heading element selection for the full rule.
On a filled brand band
onColor swaps title / subtitle / description to --text-on-color-dark for a block on a filled brand or dark surface. The block owns its own inverse story (ADR-023 amendment 2026-08-16) — never push a color override from the consumer into a slot the block renders. --text-on-color-dark is mode-invariant white, so one prop covers light and dark with no theme branch.
<ContentBlock
onColor
title="Get in touch"
description="Starting a new project or want to collaborate with us?"
actions={<Button variant="on-color">Let's Talk</Button>}
/>White on --surface-brand-primary is 3.78:1 — AA-large (3:1), not AA (4.5:1). Brand-primary fills are gated AA-large by policy (see Color Pairings, BDS-22 / ADR-015). The title is large text and unaffected; keep a band description short rather than running body copy long on the fill. Long-form copy belongs on a neutral surface.
For a centered, measure-capped section intro on a band, use SectionHeader — it forwards onColor.
Props
| Prop | Type | Default |
|---|---|---|
title | ReactNode | — |
subtitle | ReactNode | — |
description | ReactNode | — |
actions | ReactNode | — |
titleAs | 'h1' | 'h2' | 'h3' | 'h4' | 'div' | 'p' | 'h3' |
size | 'sm' | 'md' | 'lg' | 'sm' |
onColor | boolean | false |
Related
- Prose — the free-form rich-text counterpart
- Page Structure — Content blocks and prose — the spacing map both components share