Corporate page
Written for an evaluator and a procurement reviewer at the same time.
Live preview
Open the full pageSource
This exact file renders the preview above.
import ProductSwitcherNavigation from '@/components/blocks/navigation/product-switcher'
import EnterpriseHeader from '@/components/blocks/headers/enterprise'
import GridCellLogos from '@/components/blocks/sections/logos/grid-cells'
import VersusTableComparison from '@/components/blocks/sections/comparison/versus-table'
import MigrationPathComparison from '@/components/blocks/sections/comparison/migration-path'
import DividedStatsBand from '@/components/blocks/sections/stats/divided-band'
import HiringTeam from '@/components/blocks/sections/team/hiring'
import RoutedContactCards from '@/components/blocks/sections/contact/routed-cards'
import MegaFooter from '@/components/blocks/footers/mega'
/**
* Corporate page
*
* Written for two readers at once: an evaluator who wants the comparison, and
* a procurement reviewer who wants the assurances. The routed contact cards
* near the end exist because those two readers need different inboxes.
*/
export default function CorporatePattern() {
return (
<>
<ProductSwitcherNavigation />
<main>
<EnterpriseHeader />
<GridCellLogos />
<VersusTableComparison />
<DividedStatsBand />
<MigrationPathComparison />
<HiringTeam />
<RoutedContactCards />
</main>
<MegaFooter />
</>
)
}
components/patterns/corporate.tsx
import ProductSwitcherNavigation from '@/components/blocks/navigation/product-switcher'
import EnterpriseHeader from '@/components/blocks/headers/enterprise'
import GridCellLogos from '@/components/blocks/sections/logos/grid-cells'
import VersusTableComparison from '@/components/blocks/sections/comparison/versus-table'
import MigrationPathComparison from '@/components/blocks/sections/comparison/migration-path'
import DividedStatsBand from '@/components/blocks/sections/stats/divided-band'
import HiringTeam from '@/components/blocks/sections/team/hiring'
import RoutedContactCards from '@/components/blocks/sections/contact/routed-cards'
import MegaFooter from '@/components/blocks/footers/mega'
/**
* Corporate page
*
* Written for two readers at once: an evaluator who wants the comparison, and
* a procurement reviewer who wants the assurances. The routed contact cards
* near the end exist because those two readers need different inboxes.
*/
export default function CorporatePattern() {
return (
<>
<ProductSwitcherNavigation />
<main>
<EnterpriseHeader />
<GridCellLogos />
<VersusTableComparison />
<DividedStatsBand />
<MigrationPathComparison />
<HiringTeam />
<RoutedContactCards />
</main>
<MegaFooter />
</>
)
}
components/blocks/headers/enterprise.tsx
import Link from 'next/link'
import { ArrowRight, Lock, ShieldCheck, FileCheck2 } from 'lucide-react'
import { ButtonLink } from '@/components/ui/button'
import { Container } from '@/components/ui/layout'
/**
* Enterprise header
*
* Two actions of equal weight — "talk to sales" and "read the security brief"
* — because enterprise buyers arrive with two different jobs. The assurance
* strip below carries the certifications that would otherwise be buried three
* clicks deep.
*/
const assurances = [
{ icon: ShieldCheck, label: 'SOC 2 Type II', detail: 'Audited annually' },
{ icon: Lock, label: 'SSO & SCIM', detail: 'Okta, Entra, Google' },
{ icon: FileCheck2, label: 'DPA available', detail: 'EU and UK residency' },
]
export default function EnterpriseHeader() {
return (
<header className="border-b border-line bg-canvas">
<Container className="py-section">
<div className="grid gap-10 lg:grid-cols-[1.15fr_1fr] lg:items-center lg:gap-16">
<div>
<p className="label-caps text-ink-subtle">For organisations</p>
<h1 className="display-type mt-4 text-3xl leading-tight font-semibold text-ink-strong sm:text-4xl lg:text-5xl">
A design system your security review will actually pass.
</h1>
<p className="mt-5 max-w-xl text-md text-ink-muted">
Self-hosted, no telemetry, no third-party runtime. Everything ships as source you own
and can audit.
</p>
<div className="mt-8 flex flex-wrap gap-3">
<ButtonLink
href="/forms/sales-inquiry"
size="lg"
trailingIcon={<ArrowRight className="size-4" />}
>
Talk to sales
</ButtonLink>
<ButtonLink href="/docs/installation" size="lg" variant="outline">
Read the technical brief
</ButtonLink>
</div>
</div>
<dl className="flex flex-col divide-y divide-[var(--color-border-subtle)] rounded-xl border border-line bg-surface">
{assurances.map((item) => {
const Icon = item.icon
return (
<div key={item.label} className="flex items-start gap-3.5 p-5">
<span className="mt-0.5 flex size-9 shrink-0 items-center justify-center rounded-md bg-accent-soft text-accent-soft-ink">
<Icon className="size-4.5" aria-hidden="true" />
</span>
<div>
<dt className="text-sm font-semibold text-ink-strong">{item.label}</dt>
<dd className="mt-0.5 text-xs text-ink-muted">{item.detail}</dd>
</div>
</div>
)
})}
</dl>
</div>
</Container>
<div className="border-t border-line bg-surface-sunken">
<Container className="flex flex-wrap items-center justify-between gap-3 py-4">
<p className="text-sm text-ink-muted">
Procurement questions? We answer security questionnaires in five working days.
</p>
<Link
href="/forms/quote-request"
className="text-sm font-medium text-accent underline underline-offset-4"
>
Request a quote
</Link>
</Container>
</div>
</header>
)
}
components/blocks/sections/comparison/versus-table.tsx
import { Check, Minus, X } from 'lucide-react'
import { Container } from '@/components/ui/layout'
import { Badge } from '@/components/ui/badge'
import { Table } from '@/components/ui/table'
/**
* Competitive comparison table
*
* A three-way comparison against approaches rather than named competitors —
* which keeps the section honest and stops it ageing badly. Every cell is
* icon + hidden text, so the answer is never conveyed by a glyph alone.
*/
interface Row {
id: string
criterion: string
foundry: 'yes' | 'no' | 'partial'
library: 'yes' | 'no' | 'partial'
scratch: 'yes' | 'no' | 'partial'
}
const rows: Row[] = [
{
id: 'r1',
criterion: 'You own and can edit the source',
foundry: 'yes',
library: 'no',
scratch: 'yes',
},
{
id: 'r2',
criterion: 'Accessible patterns out of the box',
foundry: 'yes',
library: 'yes',
scratch: 'no',
},
{
id: 'r3',
criterion: 'No runtime styling dependency',
foundry: 'yes',
library: 'partial',
scratch: 'yes',
},
{
id: 'r4',
criterion: 'Complete page patterns included',
foundry: 'yes',
library: 'no',
scratch: 'no',
},
{
id: 'r5',
criterion: 'Upgrades arrive automatically',
foundry: 'no',
library: 'yes',
scratch: 'no',
},
{ id: 'r6', criterion: 'Time to first screen', foundry: 'yes', library: 'yes', scratch: 'no' },
]
function Answer({ value }: { value: Row['foundry'] }) {
if (value === 'yes') {
return (
<>
<Check className="mx-auto size-4 text-success" aria-hidden="true" />
<span className="sr-only">Yes</span>
</>
)
}
if (value === 'partial') {
return (
<>
<Minus className="mx-auto size-4 text-warning" aria-hidden="true" />
<span className="sr-only">Partly</span>
</>
)
}
return (
<>
<X className="mx-auto size-4 text-ink-subtle" aria-hidden="true" />
<span className="sr-only">No</span>
</>
)
}
export default function VersusTableComparison() {
return (
<section className="border-b border-line bg-canvas py-section">
<Container>
<div className="max-w-2xl">
<h2 className="display-type text-2xl font-semibold text-ink-strong sm:text-3xl">
Three ways to get a component set.
</h2>
<p className="mt-3 text-md text-ink-muted">
Compared against approaches rather than named products, so the table stays true as the
market changes.
</p>
</div>
<div className="mt-10">
<Table
caption="Foundry compared with an installed component library and building from scratch"
rows={rows}
rowKey={(row) => row.id}
columns={[
{
key: 'criterion',
header: 'Criterion',
width: '46%',
cell: (row) => <span className="text-ink">{row.criterion}</span>,
},
{
key: 'foundry',
header: (
<span className="flex items-center justify-center gap-2">
Foundry{' '}
<Badge size="sm" tone="accent">
This
</Badge>
</span>
),
align: 'center',
cell: (row) => <Answer value={row.foundry} />,
},
{
key: 'library',
header: 'Installed library',
align: 'center',
cell: (row) => <Answer value={row.library} />,
},
{
key: 'scratch',
header: 'From scratch',
align: 'center',
cell: (row) => <Answer value={row.scratch} />,
},
]}
/>
</div>
<p className="mt-4 text-xs text-ink-subtle">
Foundry deliberately does not upgrade automatically. Owning the source means owning the
changes.
</p>
</Container>
</section>
)
}
Demo source — adapt to your project. Foundry is not published as a package.
Usage
The routed contact cards near the end exist because the two readers this page serves need different inboxes.
- Put the assurances in the hero. Burying compliance three clicks deep costs deals.
- Include a comparison row you lose; it is what makes the rest credible.
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.
- Workspace switcher navigation
- Assurance-led hero
- Competitive and migration comparisons
- Routed contact cards
Accessibility
- Comparison tables
- Every cell pairs an icon with hidden text, so no answer depends on a glyph.
- Named routes
- Each contact card states its purpose and response time as 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 patternsEnterprise hero
Two equal-weight actions and an assurance panel carrying compliance facts.
starter3 variantsCompetitive comparison
A three-way comparison against approaches rather than named competitors.
intermediate3 variantsRouted contact cards
Four routes with explicit response expectations instead of one form.
starter3 variants