Patterns · Layout
Marketing sections
MarketingSection, MarketingSectionHeader, FeatureGrid, ProductFrame, StatStrip, CTABand and FAQ: the body of a public page.
Section with features
The editor
Every idea gets the right shape.
Type a slash and pick a block.
Slash commands
Tables, lists and callouts, one keystroke away.
Version history
Named snapshots you can restore safely.
import { FeatureGrid, MarketingSection, MarketingSectionHeader } from '@rookcom/monarch-react/marketing';
<MarketingSection spacing="sm" aria-labelledby="ex-features">
<MarketingSectionHeader
titleId="ex-features"
eyebrow="The editor"
title="Every idea gets the right shape."
description="Type a slash and pick a block."
/>
<FeatureGrid
columns={2}
items={[
{
title: 'Slash commands',
description: 'Tables, lists and callouts, one keystroke away.',
},
{
title: 'Version history',
description: 'Named snapshots you can restore safely.',
},
]}
/>
</MarketingSection>Usage examples
Product frame, stats, FAQ and CTA
import { Button } from '@rookcom/monarch-react';
import { CTABand, FAQ, ProductFrame, StatStrip } from '@rookcom/monarch-react/marketing';
<div style={{ display: 'grid', gap: 'var(--mn-space-10)' }}>
<ProductFrame
label="Share dialog with a copyable link"
chrome="browser"
title="example.rookhq.com/share"
>
<p style={{ margin: 0, padding: 'var(--mn-space-6)' }}>
Anyone with the link can view
</p>
</ProductFrame>
<StatStrip
items={[
{ value: '3', label: 'Export formats' },
{ value: '13', label: 'Interface languages' },
]}
/>
<FAQ
items={[
{ question: 'Which formats can I export?', answer: 'HTML, Markdown and PDF.' },
]}
/>
<CTABand
headingAs="h3"
title="Start with a blank page."
actions={<Button variant="primary">Start writing</Button>}
/>
</div>Purpose
The building blocks between hero and footer. Each is a small, opinionated pattern so product sites share rhythm, type and colour without local CSS. Import from @rookcom/monarch-react/marketing.
Use when
- MarketingSection for every band after the hero; MarketingSectionHeader at its top.
- FeatureGrid for capabilities or benefits; ProductFrame for product illustrations.
- StatStrip for a few true figures; FAQ for buyer questions; CTABand once, at the end.
Avoid when
- Signed-in app screens: use Card, StatCard, SettingsSection and the app layouts.
- In-app section headings: use the planned
section-header.
Design principles
- Rhythm from tokens: sections pad with
--mn-section-{sm,md,lg}, content sits in--mn-width-{narrow,content,wide}. - One accent: eyebrows, feature icons and the CTA band use the product accent; gradients are derived from it, never hand-picked.
tone="inverse"is the only dark band. Inside it Monarch remaps text, borders, surfaces, accent text and focus ring, all contrast-tested for every product. In dark themes it becomes an elevated surface instead of flipping to light.- Illustrations are presentational: ProductFrame is one labelled image whose contents are inert.
API reference
import { CTABand, FAQ, FeatureGrid, MarketingSection, MarketingSectionHeader, ProductFrame, StatStrip } from '@rookcom/monarch-react/marketing';| Export | Kind | Rendering | Root class |
|---|---|---|---|
CTABand | component | server-safe | .mn-cta |
FAQ | component | client | .mn-faq |
FeatureGrid | component | server-safe | .mn-feature-grid |
MarketingSection | component | server-safe | .mn-msection |
MarketingSectionHeader | component | server-safe | .mn-msection |
ProductFrame | component | server-safe | .mn-product-frame |
StatStrip | component | server-safe | .mn-stat-strip |
FeatureGrid renders a <ul>; items with href become one stretched link per card.
StatStrip renders a <dl>: label (dt) first for screen readers, value shown first visually.
FAQ wraps Accordion (type="single" collapsible unless multiple).
CTABand on tone="accent" inverts primary and secondary Buttons inside it.
Generated from the TypeScript declarations of packages/react/src/patterns/layout/marketing-section-blocks.
Properties
CTABand
A closing call to action. Server-component safe.
| Prop | Type | Description |
|---|---|---|
title required | ReactNode | |
actions | ReactNode | Buttons. On the `accent` tone, Monarch inverts Button colours automatically. |
children | ReactNode | |
className | string | |
description | ReactNode | |
headingAs | HeadingLevel | |
id | string | |
tone | "accent" | "subtle" | "inverse" | `accent` for the closing call to action; `subtle` and `inverse` for quieter moments. |
FAQ
Frequently asked questions. Answer the question a buyer actually has; never internal notes. Each question is a heading, so the list appears in the outline.
| Prop | Type | Description |
|---|---|---|
items required | FAQItem[] | |
className | string | |
defaultOpen | number | Index of the question open on first render. |
headingAs | HeadingLevel | Heading element for each question. |
multiple | boolean | Allow several answers open at once. Default false. |
FeatureGrid
Grid of features or benefits. Linked cards use one stretched link per card, so each is a single tab stop with the title as its name. Server-component safe.
| Prop | Type | Description |
|---|---|---|
items required | FeatureItem[] | |
children | ReactNode | |
className | string | |
columns | 3 | 4 | 2 | |
headingAs | HeadingLevel | Heading element for each item title. |
id | string | |
variant | "card" | "plain" | `card` adds a bordered surface; `plain` is for dense lists. |
MarketingSection
A full-width band with a centred content column. Server-component safe.
| Prop | Type | Description |
|---|---|---|
children | ReactNode | |
className | string | |
id | string | |
spacing | "sm" | "md" | "lg" | Vertical padding from the section rhythm scale. |
tone | "default" | "subtle" | "inverse" | `default` on the page canvas, `subtle` for a quiet alternate band, `inverse` for one high-emphasis band. Inside `inverse`, Monarch remaps text, borders, surfaces, accent text and focus ring, so nested components stay readable. |
width | "content" | "wide" | "narrow" | Content width. |
MarketingSectionHeader
Eyebrow, display title and description at the top of a marketing section. (In-app section headings are the planned `section-header`.) Server-component safe.
| Prop | Type | Description |
|---|---|---|
title required | ReactNode | |
actions | ReactNode | Optional link or button beside a start-aligned header. |
align | "center" | "start" | |
children | ReactNode | |
className | string | |
description | ReactNode | |
eyebrow | ReactNode | |
headingAs | HeadingLevel | Heading element; `h2` for page sections. |
id | string | |
size | "md" | "lg" | Title size from the display scale. |
titleId | string | Id for the title, so the section can use `aria-labelledby`. |
ProductFrame
Neutral frame for product illustrations. The contents are presentational: one `role="img"` with `label`, and `inert`, so sample headings and inputs are neither read out nor focusable. Server-component safe.
| Prop | Type | Description |
|---|---|---|
children required | ReactNode | The illustrated UI. Build it from Monarch components so it stays on-brand. |
label required | string | Accessible description of the illustration, e.g. "Dossier editor with an outline and the Assistant panel". Required: the frame is announced as one image. |
backdrop | boolean | A soft accent panel behind the frame, for hero moments. |
caption | ReactNode | Visible caption under the frame. |
chrome | "none" | "window" | "browser" | `window` (app window dots), `browser` (address bar) or `none`. |
className | string | |
id | string | |
title | string | Text in the chrome bar: a document title, or a URL with `chrome="browser"`. |
StatStrip
A row of key figures as a description list. Server-component safe.
| Prop | Type | Description |
|---|---|---|
items required | StatItem[] | |
children | ReactNode | |
className | string | |
id | string |
Variants
| Prop | Values | Description |
|---|---|---|
tone (MarketingSection) | 'default''subtle''inverse' | Canvas, quiet alternate band, or the one high-emphasis band. |
spacing (MarketingSection) | 'sm''md''lg' | Vertical padding from the section rhythm scale. |
width (MarketingSection) | 'narrow''content''wide' | 44rem, 72rem or 80rem content column. |
align / size (MarketingSectionHeader) | 'start | center''md | lg' | Alignment and display size of the title. |
columns / variant (FeatureGrid) | '2 | 3 | 4''card | plain' | Columns at full width; bordered cards or a plain list. |
chrome / backdrop (ProductFrame) | 'window | browser | none''true | false' | App-window dots, a URL bar or nothing; optional soft accent panel behind. |
tone (CTABand) | 'accent''subtle''inverse' | Accent gradient (default), quiet panel, or dark panel. |
States
| State | Exposed as | Behaviour |
|---|---|---|
| Linked feature hover | data-linked | Border strengthens and a medium shadow appears. |
| FAQ item open | data-state="open" | From Accordion. |
Interaction behaviour
- Only linked FeatureGrid cards, FAQ questions and the actions you pass are interactive.
- ProductFrame contents never receive focus or pointer events (
inert).
Keyboard support
| Keys | Action |
|---|---|
| Tab | Linked feature cards are one tab stop each; FAQ triggers follow. |
| Enter / Space | Opens a linked card or toggles a FAQ answer. |
| Arrow Up / Down, Home / End | Move between FAQ questions. |
Accessibility
- Label each MarketingSection with
aria-labelledbypointing at its headertitleId, so it becomes a named region. - Headings come from props (
headingAs) so the outline never skips: h2 for sections, h3 for features and questions. - ProductFrame requires
label; contents areinertso sample headings and inputs stay out of the outline and tab order. - Feature icons are decorative (
aria-hidden). - Inverse sections keep 4.5:1 text contrast (7:1 more-contrast) for every Rook product, including accent text on cards within the band.
Responsive behaviour
- FeatureGrid: one column, two from 640px, three or four from 1024px.
- StatStrip: two columns, three or four from 1024px, with hairline separators that follow the grid.
- CTABand: stacked copy and actions; side by side from 1024px.
Theme support
- Subtle bands use
--mn-color-bg-subtle; inverse bands--mn-gradient-inverse; CTA--mn-gradient-accent; all follow the product accent. - Verified in light, dark and more-contrast with the default, Dossier, Calendar and Base accents (visual baselines).
Do and don't
Do
- Alternate default and subtle bands; use inverse at most once per page.
- Build ProductFrame contents from Monarch components so illustrations stay on-brand.
- Use only true, current numbers in StatStrip.
Don't
- Do not hand-pick colours or gradients (hex values, arbitrary colour classes, near-black section fills); use the tones.
- Do not use two CTA bands or more than one primary action per section.
- Do not use sparkle icons for AI features; use a plain Bot icon.
- Do not write FAQ entries that are internal notes ("Is this implemented?").
Related components
- MarketingHero: The section that opens the page.
- SiteHeader and SiteFooter: Header and footer around the sections.
- Accordion: The disclosure FAQ is built on.
- StatCard: Figures inside signed-in dashboards.
Dependencies
| Kind | Depends on |
|---|---|
| npm packages | None |
| Monarch components | Accordion |
Tokens: --mn-display-*, --mn-section-*, --mn-width-*, --mn-gradient-{wash,accent,inverse}, --mn-color-*-inverse*, --mn-color-accent-strong-inverse.
Testing coverage
- All blocks compose with no axe violations and render on the server.
- MarketingSection sets tone, spacing and width and is named by its h2.
- ProductFrame is one labelled image with inert contents.
- Linked FeatureGrid cards are one tab stop named by the title.
- StatStrip pairs dt labels with dd values.
- FAQ honours defaultOpen and toggles from the keyboard.
- CTABand heading level and tone.
| Check | Covered |
|---|---|
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | Visual baselines: /__marketing in light, dark and more-contrast × 4 accents × 1280/390; Three-browser axe: /__marketing |
Tests: packages/react/src/patterns/layout/marketing-section-blocks