Numbered process steps
Six ordered cards with durations attached.
Live preview
Source
This exact file renders the preview above.
import { Container } from '@/components/ui/layout'
import { Badge } from '@/components/ui/badge'
import { processSteps } from '@/content/demo'
/**
* Numbered process steps
*
* A twelve-week engagement as six ordered cards. Rendered as an `<ol>` because
* the order is the content — and each step carries a duration, which is the
* question clients ask before they ask anything else.
*/
export default function NumberedProcessSteps() {
return (
<section className="border-b border-line bg-canvas py-section">
<Container>
<div className="max-w-2xl">
<p className="label-caps text-accent">How we work</p>
<h2 className="display-type mt-3 text-2xl font-semibold text-ink-strong sm:text-3xl">
Twelve weeks, six stages, no surprises.
</h2>
</div>
<ol className="mt-12 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{processSteps.map((step, index) => (
<li
key={step.title}
className="flex flex-col rounded-xl border border-line bg-surface p-6"
>
<div className="flex items-center justify-between gap-3">
<span className="display-type text-2xl font-semibold text-ink-subtle tabular-nums">
{String(index + 1).padStart(2, '0')}
</span>
{step.duration ? <Badge size="sm">{step.duration}</Badge> : null}
</div>
<h3 className="mt-4 text-md font-semibold text-ink-strong">{step.title}</h3>
<p className="mt-2 text-sm leading-relaxed text-ink-muted">{step.description}</p>
</li>
))}
</ol>
</Container>
</section>
)
}
components/blocks/sections/process/numbered-steps.tsx
import { Container } from '@/components/ui/layout'
import { Badge } from '@/components/ui/badge'
import { processSteps } from '@/content/demo'
/**
* Numbered process steps
*
* A twelve-week engagement as six ordered cards. Rendered as an `<ol>` because
* the order is the content — and each step carries a duration, which is the
* question clients ask before they ask anything else.
*/
export default function NumberedProcessSteps() {
return (
<section className="border-b border-line bg-canvas py-section">
<Container>
<div className="max-w-2xl">
<p className="label-caps text-accent">How we work</p>
<h2 className="display-type mt-3 text-2xl font-semibold text-ink-strong sm:text-3xl">
Twelve weeks, six stages, no surprises.
</h2>
</div>
<ol className="mt-12 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{processSteps.map((step, index) => (
<li
key={step.title}
className="flex flex-col rounded-xl border border-line bg-surface p-6"
>
<div className="flex items-center justify-between gap-3">
<span className="display-type text-2xl font-semibold text-ink-subtle tabular-nums">
{String(index + 1).padStart(2, '0')}
</span>
{step.duration ? <Badge size="sm">{step.duration}</Badge> : null}
</div>
<h3 className="mt-4 text-md font-semibold text-ink-strong">{step.title}</h3>
<p className="mt-2 text-sm leading-relaxed text-ink-muted">{step.description}</p>
</li>
))}
</ol>
</Container>
</section>
)
}
content/demo.ts
/**
* Shared demo content.
*
* Marketing copy lives here rather than inside JSX, so a section file stays a
* layout decision and nothing else. Every section in the library draws from
* this module, which is also what keeps 110 sections from becoming 110 copies
* of the same paragraph.
*
* All names, companies and figures are fictional.
*/
export interface Feature {
title: string
description: string
/** lucide-react icon name, resolved by the consuming section. */
icon: string
}
export const features: Feature[] = [
{
title: 'Token-driven theming',
description:
'Three orthogonal axes — scheme, palette, density — compose into every surface. Change one and the whole system follows.',
icon: 'Palette',
},
{
title: 'Accessible by construction',
description:
'Focus management, live regions and keyboard contracts are built into the primitives, not bolted on during review.',
icon: 'Accessibility',
},
{
title: 'Server-first rendering',
description:
'Sections are Server Components. Interactivity is scoped to small client islands, so a marketing page ships almost no JavaScript.',
icon: 'Server',
},
{
title: 'Composable sections',
description:
'Every block is a standalone export. Assemble a page from nine of them, or lift one into an existing codebase.',
icon: 'Layers',
},
{
title: 'Real source, always',
description:
'The code viewer reads the same file that renders the preview. Documentation cannot drift from implementation.',
icon: 'Code2',
},
{
title: 'Built to grow',
description:
'Adding a component means adding a catalogue record and a file. No page in the application needs to change.',
icon: 'Sprout',
},
]
export const secondaryFeatures: Feature[] = [
{
title: 'Responsive previews',
description: 'Inspect any block at 390, 768 and 1440 without leaving the page.',
icon: 'Smartphone',
},
{
title: 'Deterministic copy',
description: 'The copy button copies exactly what the viewer displays — no rewriting.',
icon: 'Clipboard',
},
{
title: 'Typed catalogue',
description: 'One record shape across six families, validated by the test suite.',
icon: 'ListTree',
},
{
title: 'Zero runtime CSS',
description: 'Tailwind v4 plus custom properties. No styling library in the bundle.',
icon: 'Feather',
},
{
title: 'Keyboard everything',
description: 'Menus, tabs, dialogs and the palette all follow published ARIA patterns.',
icon: 'Keyboard',
},
{
title: 'Dark authored, not inverted',
description: 'The dark scheme is designed, not derived from a filter.',
icon: 'Moon',
},
]
export interface Stat {
value: string
label: string
detail?: string
}
export const stats: Stat[] = [
{ value: '4', label: 'Composition levels', detail: 'Primitives through complete products' },
{ value: '3', label: 'Theme axes', detail: 'Scheme, palette, density' },
{ value: '0', label: 'Runtime dependencies', detail: 'Beyond React, Next and icons' },
{ value: '100%', label: 'Statically rendered', detail: 'Every catalogue route' },
]
export const companyLogos = [
'Northwind',
'Halcyon',
'Meridian',
'Kestrel',
'Lumen Works',
'Atlas Forge',
'Verdant',
'Ironwood',
]
export interface Testimonial {
quote: string
name: string
role: string
company: string
}
export const testimonials: Testimonial[] = [
{
quote:
'We replaced four half-finished internal libraries with one. The density axis alone paid for the migration — our admin tools and our marketing site finally share components.',
name: 'Priya Raman',
role: 'Principal Engineer',
company: 'Northwind',
},
{
quote:
'The accessibility work is the part I did not expect. Focus return, live regions, manual tab activation — the things that normally get filed as tech debt were already handled.',
name: 'Tomas Lindqvist',
role: 'Head of Design Systems',
company: 'Halcyon',
},
{
quote:
'Being able to read the exact source that renders the preview removed an entire class of "the docs are wrong" tickets.',
name: 'Amara Osei',
role: 'Staff Frontend Engineer',
company: 'Meridian',
},
{
quote:
'Our first landing page took an afternoon. The second took forty minutes, because by then we were composing rather than building.',
name: 'Jun Watanabe',
role: 'Product Lead',
company: 'Kestrel',
},
{
quote:
'Five palettes sounds like a gimmick until a client asks for a different accent on the third day and it is a one-line change.',
name: 'Elena Rossi',
role: 'Creative Director',
company: 'Lumen Works',
},
{
quote:
'The starters are the honest part. They are not screenshots — every route resolves, including the 404s.',
name: 'Marcus Bell',
role: 'Engineering Manager',
company: 'Atlas Forge',
},
]
export interface PricingTier {
name: string
price: string
cadence: string
description: string
features: string[]
cta: string
featured?: boolean
note?: string
}
export const pricingTiers: PricingTier[] = [
{
name: 'Solo',
price: '$0',
cadence: 'forever',
description: 'For personal projects and evaluation.',
features: [
'Every component and section',
'Copy-paste source',
'Light and dark schemes',
'Community support',
],
cta: 'Start building',
},
{
name: 'Team',
price: '$18',
cadence: 'per seat / month',
description: 'For product teams shipping together.',
features: [
'Everything in Solo',
'All five palettes',
'Starter products',
'Figma token export',
'Priority issue triage',
],
cta: 'Start a trial',
featured: true,
note: 'Most teams start here.',
},
{
name: 'Business',
price: '$2,400',
cadence: 'per year',
description: 'For organisations with a design-system function.',
features: [
'Everything in Team',
'Unlimited seats',
'Private component registry',
'Accessibility review sessions',
'Named support contact',
],
cta: 'Contact sales',
},
]
export interface FAQ {
question: string
answer: string
}
export const faqs: FAQ[] = [
{
question: 'Is Foundry a published npm package?',
answer:
'No. Foundry is a template demonstration. Components are copied into your project and owned by you — there is no registry, no install command and no version to upgrade.',
},
{
question: 'How do I change the accent colour?',
answer:
'Every accent in the system resolves from one ramp of custom properties. Redefine those seven values in your stylesheet and every button, focus ring, link and badge follows.',
},
{
question: 'Do the components require JavaScript?',
answer:
'Most do not. Sections, cards, tables, forms and layout primitives render entirely on the server. Only overlays, menus and the command palette are client islands.',
},
{
question: 'What does the density axis actually change?',
answer:
'Control heights, control padding, stack rhythm, card padding, table row height and section padding. It deliberately does not change the type scale, so text stays legible at every density.',
},
{
question: 'Can I use only part of the library?',
answer:
'Yes. Each block is a standalone file whose imports are visible in the code viewer. Copying one section brings the primitives it names and nothing else.',
},
{
question: 'How is the source in the documentation kept accurate?',
answer:
'A build step reads the real files from disk and generates the source registry. The preview and the code you copy come from the same file, and a test fails if the two ever diverge.',
},
{
question: 'Is there a Figma library?',
answer:
'The token manifest is designed to be exported to Figma variables, but no Figma file ships with this demo.',
},
{
question: 'What browsers are supported?',
answer:
'Current versions of Chrome, Edge, Safari and Firefox. The library uses container-free responsive CSS and standard custom properties throughout.',
},
]
export interface TeamMember {
name: string
role: string
bio: string
location: string
}
export const team: TeamMember[] = [
{
name: 'Priya Raman',
role: 'Design systems',
bio: 'Spent six years untangling component libraries before deciding to write one properly.',
location: 'Bengaluru',
},
{
name: 'Tomas Lindqvist',
role: 'Accessibility',
bio: 'Audits by day, writes focus-management utilities by night.',
location: 'Stockholm',
},
{
name: 'Amara Osei',
role: 'Frontend architecture',
bio: 'Believes most performance problems are really architecture problems.',
location: 'Accra',
},
{
name: 'Jun Watanabe',
role: 'Product',
bio: 'Turns "we should document this" into things that are actually documented.',
location: 'Osaka',
},
{
name: 'Elena Rossi',
role: 'Visual design',
bio: 'Editorial background; still measures leading by eye and is usually right.',
location: 'Milan',
},
{
name: 'Marcus Bell',
role: 'Developer experience',
bio: 'Optimises for the twentieth time you use something, not the first.',
location: 'Manchester',
},
{
name: 'Sofia Delgado',
role: 'Documentation',
bio: 'Thinks a component without a usage note is only half shipped.',
location: 'Madrid',
},
{
name: 'Idris Karim',
role: 'Infrastructure',
bio: 'Keeps the build under a minute so nobody is tempted to skip it.',
location: 'Toronto',
},
]
export interface ProcessStep {
title: string
description: string
duration?: string
}
export const processSteps: ProcessStep[] = [
{
title: 'Audit',
description:
'Inventory every surface, control and one-off in the existing product. Nothing is designed before the mess is measured.',
duration: 'Week 1–2',
},
{
title: 'Tokenise',
description:
'Extract the real colour, type and spacing decisions into a token set the whole team can read.',
duration: 'Week 3',
},
{
title: 'Build primitives',
description:
'Ship the twenty components that account for eighty per cent of screens, with states and keyboard contracts.',
duration: 'Week 4–7',
},
{
title: 'Compose',
description:
'Assemble sections and page patterns, then migrate a real route to prove the system holds.',
duration: 'Week 8–10',
},
{
title: 'Document',
description:
'Write the usage guidance while the decisions are fresh, and wire the docs to the source.',
duration: 'Week 11',
},
{
title: 'Hand over',
description:
'Contribution guide, review checklist, and a session on how to add the twenty-first component.',
duration: 'Week 12',
},
]
export interface TimelineEvent {
year: string
title: string
description: string
}
export const timelineEvents: TimelineEvent[] = [
{
year: '2021',
title: 'One repository, four libraries',
description:
'Four product teams, four component sets, four opinions about what "medium" meant.',
},
{
year: '2022',
title: 'The first token pass',
description: 'Colour and spacing consolidated. Everything else stayed where it was.',
},
{
year: '2023',
title: 'Primitives extracted',
description:
'Twenty components, one focus treatment, one control height. Adoption became easier than resistance.',
},
{
year: '2024',
title: 'Sections and patterns',
description: 'Marketing stopped rebuilding heroes and started composing them.',
},
{
year: '2025',
title: 'Density as an axis',
description: 'Admin tools and marketing pages finally shared a component set.',
},
{
year: '2026',
title: 'Foundry 1.0',
description: 'Four levels, one system, documented against its own source.',
},
]
export interface CaseStudy {
slug: string
client: string
title: string
summary: string
sector: string
year: string
metrics: Array<{ label: string; value: string }>
}
export const caseStudies: CaseStudy[] = [
{
slug: 'northwind-console',
client: 'Northwind',
title: 'One console for eleven internal tools',
summary:
'Eleven admin surfaces, each with its own table implementation, consolidated into a single shell with a shared data layer.',
sector: 'Logistics',
year: '2025',
metrics: [
{ label: 'Tools consolidated', value: '11' },
{ label: 'Bundle reduction', value: '61%' },
{ label: 'Time to new screen', value: '2 days' },
],
},
{
slug: 'halcyon-storefront',
client: 'Halcyon',
title: 'A storefront that loads on a train',
summary:
'Rebuilt a client-rendered catalogue as server components with small islands, targeting the worst connection the team could find.',
sector: 'Retail',
year: '2025',
metrics: [
{ label: 'JS shipped', value: '−78%' },
{ label: 'LCP, 3G', value: '1.4s' },
{ label: 'Conversion', value: '+9%' },
],
},
{
slug: 'meridian-docs',
client: 'Meridian',
title: 'Documentation that cannot go stale',
summary:
'Wired the documentation site to the component source so every example is generated from the file it documents.',
sector: 'Developer tools',
year: '2024',
metrics: [
{ label: 'Stale examples', value: '0' },
{ label: 'Docs PRs / month', value: '×3' },
{ label: 'Support tickets', value: '−34%' },
],
},
{
slug: 'kestrel-onboarding',
client: 'Kestrel',
title: 'Onboarding in four steps instead of nine',
summary:
'Reduced a nine-screen signup to four, with real validation, resumable progress and a working keyboard path throughout.',
sector: 'Fintech',
year: '2024',
metrics: [
{ label: 'Steps removed', value: '5' },
{ label: 'Completion', value: '+27%' },
{ label: 'Support contacts', value: '−41%' },
],
},
{
slug: 'lumen-rebrand',
client: 'Lumen Works',
title: 'A rebrand in one pull request',
summary:
'Because every surface resolved from tokens, a full visual rebrand touched one file and shipped in a single review.',
sector: 'Media',
year: '2023',
metrics: [
{ label: 'Files changed', value: '1' },
{ label: 'Screens updated', value: '240+' },
{ label: 'Regressions', value: '0' },
],
},
]
export interface Post {
slug: string
title: string
excerpt: string
category: string
date: string
readingTime: string
author: string
}
export const posts: Post[] = [
{
slug: 'density-as-an-axis',
title: 'Density belongs in your token system',
excerpt:
'Most design systems treat compact mode as a table prop. Making it a theme axis is what finally lets admin tools and marketing pages share components.',
category: 'Design systems',
date: '2026-03-12',
readingTime: '8 min',
author: 'Priya Raman',
},
{
slug: 'manual-tab-activation',
title: 'Why your tabs should not auto-activate',
excerpt:
'Automatic activation feels responsive and quietly punishes keyboard users. A short argument for the boring default.',
category: 'Accessibility',
date: '2026-02-27',
readingTime: '6 min',
author: 'Tomas Lindqvist',
},
{
slug: 'documentation-from-source',
title: 'Generate documentation from the file, not about it',
excerpt:
'A build step that reads the real component and a test that fails on drift removes an entire category of wrong documentation.',
category: 'Tooling',
date: '2026-02-09',
readingTime: '5 min',
author: 'Sofia Delgado',
},
{
slug: 'server-components-marketing',
title: 'Marketing pages do not need a framework runtime',
excerpt:
'What actually needs to be interactive on a landing page, and how small the remaining island can be.',
category: 'Performance',
date: '2026-01-22',
readingTime: '9 min',
author: 'Amara Osei',
},
{
slug: 'variants-that-earn-their-place',
title: 'A variant should represent a decision, not a padding value',
excerpt:
'How to tell a real variant from an inflated catalogue, and why one hundred good components beat three hundred similar ones.',
category: 'Design systems',
date: '2026-01-08',
readingTime: '7 min',
author: 'Elena Rossi',
},
{
slug: 'focus-return',
title: 'The half of focus management everyone forgets',
excerpt:
'Trapping focus in a dialog is the easy part. Putting it back where it came from is what keyboard users actually notice.',
category: 'Accessibility',
date: '2025-12-15',
readingTime: '4 min',
author: 'Tomas Lindqvist',
},
]
export interface Integration {
name: string
category: string
description: string
}
export const integrations: Integration[] = [
{
name: 'Vector',
category: 'Analytics',
description: 'Event streaming with a typed schema registry.',
},
{
name: 'Cinder',
category: 'Monitoring',
description: 'Traces, logs and alerts in one timeline.',
},
{
name: 'Postmark',
category: 'Email',
description: 'Transactional delivery with per-template metrics.',
},
{ name: 'Harbour', category: 'Storage', description: 'Object storage with signed upload URLs.' },
{
name: 'Sentinel',
category: 'Security',
description: 'Dependency and secret scanning on every push.',
},
{ name: 'Ledger', category: 'Billing', description: 'Usage metering and invoice generation.' },
{
name: 'Relay',
category: 'Messaging',
description: 'Webhooks with automatic retry and replay.',
},
{
name: 'Atlas',
category: 'Search',
description: 'Typo-tolerant search with faceted filtering.',
},
{ name: 'Beacon', category: 'Support', description: 'Shared inbox with conversation routing.' },
{ name: 'Quarry', category: 'Data', description: 'Warehouse sync with incremental models.' },
{ name: 'Pilot', category: 'Deployment', description: 'Preview environments per pull request.' },
{ name: 'Lantern', category: 'Documentation', description: 'Docs generated from typed schemas.' },
]
export interface Product {
slug: string
name: string
category: string
priceCents: number
compareAtCents?: number
description: string
material: string
badge?: string
inStock: boolean
rating: number
reviews: number
}
export const products: Product[] = [
{
slug: 'field-shell-jacket',
name: 'Field Shell Jacket',
category: 'outerwear',
priceCents: 32800,
compareAtCents: 41000,
description:
'A three-layer shell cut close enough to wear in a city and sealed well enough not to.',
material: 'Recycled ripstop',
badge: 'Last season pricing',
inStock: true,
rating: 4.7,
reviews: 214,
},
{
slug: 'quarry-overshirt',
name: 'Quarry Overshirt',
category: 'outerwear',
priceCents: 18500,
description: 'Heavyweight cotton twill that behaves like a jacket and packs like a shirt.',
material: 'Organic cotton twill',
inStock: true,
rating: 4.5,
reviews: 132,
},
{
slug: 'meridian-knit',
name: 'Meridian Merino Knit',
category: 'knitwear',
priceCents: 14900,
description: 'Fine-gauge merino with a high neck and no branding anywhere on it.',
material: 'Extrafine merino',
badge: 'Restocked',
inStock: true,
rating: 4.8,
reviews: 341,
},
{
slug: 'harbour-cardigan',
name: 'Harbour Cardigan',
category: 'knitwear',
priceCents: 21000,
description: 'A boxy cardigan with horn buttons and a shawl collar that holds its shape.',
material: 'Lambswool',
inStock: false,
rating: 4.4,
reviews: 88,
},
{
slug: 'atlas-chino',
name: 'Atlas Chino',
category: 'trousers',
priceCents: 12800,
compareAtCents: 15900,
description: 'A straight-leg chino with a proper waistband and pockets that hold a phone.',
material: 'Compact cotton',
inStock: true,
rating: 4.6,
reviews: 507,
},
{
slug: 'ironwood-trouser',
name: 'Ironwood Wool Trouser',
category: 'trousers',
priceCents: 24500,
description: 'Pleated, cuffed, and cut for people who sit down during the day.',
material: 'Wool flannel',
inStock: true,
rating: 4.3,
reviews: 76,
},
{
slug: 'lumen-tote',
name: 'Lumen Tote',
category: 'accessories',
priceCents: 9800,
description: 'Waxed canvas with a leather base and a strap long enough for a coat.',
material: 'Waxed canvas',
badge: 'Best seller',
inStock: true,
rating: 4.9,
reviews: 623,
},
{
slug: 'kestrel-cap',
name: 'Kestrel Six-Panel Cap',
category: 'accessories',
priceCents: 5200,
description: 'Unstructured, cotton-lined, with a brim that survives being sat on.',
material: 'Cotton canvas',
inStock: true,
rating: 4.2,
reviews: 195,
},
{
slug: 'verdant-tee',
name: 'Verdant Heavy Tee',
category: 'basics',
priceCents: 4800,
description: 'A 240gsm tee that holds its collar past the first wash.',
material: 'Organic cotton',
inStock: true,
rating: 4.6,
reviews: 1102,
},
{
slug: 'halcyon-socks',
name: 'Halcyon Ribbed Socks',
category: 'basics',
priceCents: 2200,
description: 'Three pairs. Ribbed, reinforced, and the same length as each other.',
material: 'Merino blend',
inStock: true,
rating: 4.5,
reviews: 289,
},
]
export const productCategories = [
{ slug: 'outerwear', name: 'Outerwear', description: 'Shells, overshirts and coats.' },
{ slug: 'knitwear', name: 'Knitwear', description: 'Merino, lambswool and cotton knits.' },
{ slug: 'trousers', name: 'Trousers', description: 'Chinos, wool and workwear cuts.' },
{ slug: 'accessories', name: 'Accessories', description: 'Bags, caps and small goods.' },
{ slug: 'basics', name: 'Basics', description: 'Tees, socks and everyday layers.' },
]
export interface MenuDish {
name: string
description: string
price: string
tags?: string[]
}
export interface MenuCourse {
course: string
note?: string
dishes: MenuDish[]
}
export const restaurantMenu: MenuCourse[] = [
{
course: 'To begin',
note: 'Served from opening until close.',
dishes: [
{
name: 'Cured trout, fennel, dill oil',
description: 'Two-day cure, shaved fennel, dill pressed the same morning.',
price: '16',
tags: ['GF'],
},
{
name: 'Charred leek, hazelnut, aged sheep cheese',
description: 'Leeks over embers until the outer layer gives up entirely.',
price: '14',
tags: ['V'],
},
{
name: 'Sourdough, cultured butter',
description: 'Four-day starter, butter churned in-house on Tuesdays.',
price: '7',
tags: ['V'],
},
],
},
{
course: 'Mains',
dishes: [
{
name: 'Aged duck breast, quince, bitter leaves',
description: 'Dry-aged fourteen days, quince from the orchard at Ashfield.',
price: '34',
},
{
name: 'Turbot on the bone, brown butter, capers',
description: 'Whole fish for two, carved at the table.',
price: '58',
tags: ['For two'],
},
{
name: 'Barley, wild mushroom, preserved lemon',
description: 'Slow-cooked barley with a mushroom broth reduced for six hours.',
price: '26',
tags: ['V', 'Vegan on request'],
},
],
},
{
course: 'To finish',
dishes: [
{
name: 'Burnt honey tart, crème fraîche',
description: 'Honey taken just past the point most people would stop.',
price: '12',
tags: ['V'],
},
{
name: 'Poached pear, walnut, blue cheese',
description: 'Pears poached in the last of the autumn cider.',
price: '13',
tags: ['V'],
},
{
name: 'Selection of British cheese',
description: 'Three cheeses, oat crackers, quince paste.',
price: '16',
tags: ['V'],
},
],
},
]
export const changelog = [
{
version: '1.0.0',
date: '2026-03-14',
title: 'Foundry 1.0',
summary:
'The first complete release: four composition levels, one token system, one catalogue.',
changes: [
{
kind: 'added' as const,
text: '45 primitives across actions, forms, feedback, overlay, navigation, data and layout.',
},
{
kind: 'added' as const,
text: '15 navigation patterns, 12 header systems and 10 footer systems.',
},
{
kind: 'added' as const,
text: '28 form flows with real validation, first-invalid focus and live error announcement.',
},
{
kind: 'added' as const,
text: '112 sections spanning marketing, commerce, SaaS, content, application and state.',
},
{
kind: 'added' as const,
text: '10 complete page patterns and 10 multi-route starter products.',
},
{
kind: 'added' as const,
text: 'Theme playground with five palettes and three densities, persisted and flash-free.',
},
],
},
{
version: '0.9.0',
date: '2026-02-20',
title: 'Starters and the composition playground',
summary: 'Level 4 arrives, and sections become assemblable rather than merely browsable.',
changes: [
{
kind: 'added' as const,
text: 'Ten starter products, each with working navigation and its own 404 path.',
},
{ kind: 'added' as const, text: 'URL-driven section composition playground.' },
{
kind: 'changed' as const,
text: 'Catalogue records gained `relatedItems`, validated to resolve.',
},
{
kind: 'fixed' as const,
text: 'Drawer no longer restored focus to a detached element after route changes.',
},
],
},
{
version: '0.8.0',
date: '2026-01-30',
title: 'Density becomes an axis',
summary: 'Compact, Default and Relaxed move out of component props and into the token layer.',
changes: [
{
kind: 'added' as const,
text: 'Density tokens for control height, padding, stack rhythm and row height.',
},
{
kind: 'changed' as const,
text: 'Every control now derives its height from `--density-control-height`.',
},
{
kind: 'removed' as const,
text: 'Per-component `compact` props, superseded by the density axis.',
},
],
},
{
version: '0.7.0',
date: '2026-01-12',
title: 'Source registry',
summary: 'Documentation is generated from the files it documents.',
changes: [
{
kind: 'added' as const,
text: 'Build-time source registry; the viewer reads the real file.',
},
{
kind: 'added' as const,
text: 'Drift test that regenerates the registry and fails on mismatch.',
},
{
kind: 'fixed' as const,
text: 'Copy button now falls back to a hidden textarea on insecure origins.',
},
],
},
{
version: '0.6.0',
date: '2025-12-04',
title: 'Accessibility pass',
summary: 'Focus management and live regions moved into the primitives.',
changes: [
{
kind: 'added' as const,
text: 'Focus trap with guaranteed focus return across Dialog, Drawer and the palette.',
},
{ kind: 'changed' as const, text: 'Tabs switched to manual activation.' },
{ kind: 'fixed' as const, text: 'Tooltips now open on keyboard focus, not only on hover.' },
{
kind: 'fixed' as const,
text: 'Toast queue announces in order instead of interrupting itself.',
},
],
},
]
export const faqShort = faqs.slice(0, 4)
Demo source — adapt to your project. Foundry is not published as a package.
Usage
Rendered as an ordered list because the order is the content. Each step carries a duration, which is the question clients ask before they ask anything else.
- Attach a duration to every step or to none; a partial timeline reads as evasion.
- Zero-pad the ordinals so the numerals align.
Variants and states
Every entry below is a genuine difference in behaviour or layout, and every one of them is visible in the preview above.
- Three columns at lg
- Ordered list
- Duration badges
Accessibility
- Ordered list
- Position and count are structural.
- Badge pairing
- Durations are badges beside the ordinal, not floating text.
Foundry implements published ARIA patterns and is tested against them. No WCAG certification is claimed — see the accessibility documentation for what is and is not covered.
Related
All sectionsConnected process flow
A horizontal flow with border-drawn connectors, collapsing to a vertical rail.
starter3 variantsNumbered feature list
An ordered list for features that are genuinely sequential or ranked.
starter3 variantsProcess with deliverables
Each stage lists what the client actually receives.
starter3 variants