Containers, sections, page intros and frames that hold everything else.
A few wrappers that set width, rhythm and the quiet grey grounds between sections.
6 parts
A centred page column with a max width and side gutters. Container serves the home pages, Wrap the kit pages.
import type { ReactNode } from 'react'import { Container } from '@rchursin/home/ui'import { Wrap } from '@rchursin/product/shell/layout'
/** * Each band is the full page width. The hatched strips are the side gutters; the solid bar is * the column your content gets. Past the max width the column stops growing and centres. */export default function ContainerGutters() { return ( <div className="flex w-full flex-col gap-7 py-7"> <Band name="Container" spec="max-w-7xl · px-6 lg:px-8"> <Container> <Column>Content column</Column> </Container> </Band> <Band name="Container narrow" spec="max-w-3xl · px-6 lg:px-8"> <Container narrow> <Column>Reading column</Column> </Container>The top of a kit page: an eyebrow, the page’s h1, a lead and a row of facts, over film grain and a soft ceiling light.
import { ButtonLink, delay } from '@rchursin/home/ui'import { KitPage, PageIntro, Section } from '@rchursin/product/shell/layout'
const parts: [string, string][] = [ ['Eyebrow', 'A mono label that says where you are.'], ['Title', 'The page’s one <h1>, arriving out of a soft blur.'], ['Lead', 'Two or three sentences on what the page is for.'], ['Facts', 'Optional big numbers in a row, as a description list.'],]
/** * A whole demo page: KitPage arms the arrivals, PageIntro renders the page's <h1>, * and Sections follow below on alternating grounds. */export default function PageIntroKitPage() { return ( <KitPage> <main> <PageIntro eyebrow="Demo page · Layout"A page band with an eyebrow, an h2 and a lead, on a plain or sunken ground. SectionHeading is the heading block alone.
import type { ReactNode } from 'react'import { Section } from '@rchursin/product/shell/layout'
/** * Two sections back to back: plain, then sunken. Each one is a <section> named by its own <h2>. * The dashed outlines and corner tags are only here to show where each section starts and ends. */export default function SectionTones() { return ( <div className="w-full"> <Section id="section-demo-plain" eyebrow="01 · Plain" title="Sections set the rhythm of a page." lead="Generous space above and below, and a heading block that never grows wider than a comfortable line." className="relative outline-1 -outline-offset-1 outline-line-strong outline-dashed" > <Tag>tone="plain"</Tag> <Content /> </Section>One part on a quiet raised card. Specimen captions it underneath; Figure titles it on top.
import { useState } from 'react'import { Specimen } from '@rchursin/product/shell/layout'import { Button, Chip, KeyCombo, Switch } from '@rchursin/product/ui'
/** One part per card, named underneath. The caption is the figure's <figcaption>. */export default function SpecimenGrid() { const [sync, setSync] = useState(true) return ( <div className="grid w-full grid-cols-1 gap-x-5 gap-y-8 sm:grid-cols-2"> <Specimen label="Button · primary" note="One per view, for the main action."> <Button icon="plus">New task</Button> </Specimen> <Specimen label="Switch" note="Takes effect at once; no Save button."> <Switch label="Sync calendar" checked={sync} onChange={setSync} className="w-full max-w-56" /> </Specimen> <Specimen label="Chip · four shapes" note="Kinds told apart by shape, not colour." bodyClassName="flex-wrap gap-2"> <Chip variant="solid">Solid</Chip> <Chip>Outline</Chip> <Chip variant="muted">Muted</Chip> <Chip variant="dashed">Dashed</Chip> </Specimen> <Specimen label="Key combo" note="“mod” is ⌘ on Apple keyboards, Ctrl elsewhere."> <KeyCombo combo="mod+k" /> </Specimen> </div> )}import { Separator } from '@rchursin/product/ui'
/** Horizontal between blocks, vertical between items in a row. Both are 1px of the line token. */export default function SeparatorBasic() { return ( <div className="lux-card w-full max-w-sm rounded-2xl bg-raised p-5"> <h3 className="text-[15px] font-semibold tracking-[-0.01em]">Weekly review</h3> <p className="mt-1 text-[13px]/6 text-fg-3">Every Friday at 4:00 PM, 30 minutes.</p> <Separator className="my-4" /> <div className="flex h-5 items-center gap-3 text-[13px] text-fg-2"> <span>6 people</span> <Separator vertical /> <span>Video call</span> <Separator vertical /> <span>Repeats weekly</span> </div> </div> )}The RChursin_ app frame: a sidebar with the tools, a title row with a toolbar, and your tool in the body.
import { Icon, type IconName } from '@rchursin/product/icons/Icon'import { SampleBadge } from '@rchursin/product/shell/layout'import { AppWindow } from '@rchursin/product/shell/AppWindow'import type { KitSlug } from '@rchursin/product/shell/pages'import { Button } from '@rchursin/product/ui'
const tasks: { id: string; title: string; status: IconName; state: string }[] = [ { id: 'AGT-112', title: 'Merge api-docs into openapi-writer', status: 'status-progress', state: 'In progress' }, { id: 'AGT-108', title: 'Rewrite the ui-snapshot description', status: 'status-review', state: 'In review' }, { id: 'AGT-104', title: 'Find out why test-fixer retries twice', status: 'status-todo', state: 'Todo' }, { id: 'AGT-101', title: 'Remove db-migration-helper', status: 'status-done', state: 'Done' }, { id: 'AGT-097', title: 'Turn on tool details in telemetry', status: 'status-done', state: 'Done' }, { id: 'AGT-093', title: 'List skills nobody has run in 60 days', status: 'status-done', state: 'Done' }, { id: 'AGT-090', title: 'Archive the legacy-refactor agent', status: 'status-canceled', state: 'Canceled' }, { id: 'AGT-086', title: 'Write owners for every skill', status: 'status-done', state: 'Done' }, { id: 'AGT-081', title: 'Count skills across all repos', status: 'status-done', state: 'Done' }, { id: 'AGT-077', title: 'Agree on a naming rule for agents', status: 'status-backlog', state: 'Backlog' },]
/** Here each tool links to its page in this Library; in your app, return your own routes */max-w-7xl · px-6 lg:px-8max-w-3xl · px-6 lg:px-8max-w-7xl · px-4 sm:px-6 lg:px-8Demo page · Layout
The top of every kit page: an eyebrow, a large headline, a short lead and a row of facts, over film grain and a soft ceiling light.
01 · Parts
Each piece arrives a step after the one before it.
01 · Plain
Generous space above and below, and a heading block that never grows wider than a comfortable line.
02 · Sunken
Sunken sections get film grain, so the change of ground reads without a rule between them.
Every Friday at 4:00 PM, 30 minutes.
Workspace