/** * The English catalogue — and the source of truth for the shape of the other two. * * `Messages` is `typeof en`, so `az.tsx` and `ru.tsx` are typed against this * object and a missing or misspelt key is a `pnpm typecheck` failure rather than * a string that silently stays English at runtime. Add a key here first. * * Why a `.tsx` module and not a `.json` file: several of the site's paragraphs * carry `` mid-sentence and several interpolate a count, so the values are * `string`, `ReactNode` and `(…args) => string | ReactNode` — none of which JSON * can hold. The same reason there is no `t('a.b.c')` lookup: reading the object * directly (`m.nav.overview`) means TypeScript checks every key at the call site * and no runtime "key not found" fallback has to exist. * * What deliberately stays untranslated: component names, slugs, import lines, * prop names, token names, Figma set names — they are API surface, not copy — * and the demo content inside `registry/*` compositions, because `chrome/lib/codegen.ts` * serialises those exact trees into the snippets users copy. */ export const en = { common: { /** Accessible name of the language switcher in the header. */ language: 'Language', copy: 'Copy', copied: 'Copied', close: 'Close', /** Suspense fallback while a lazily-loaded page arrives. */ loading: 'Loading…', /** * The first thing in the tab order. Forty-odd rail links stand between the * top of the document and the page, which is the case a skip link is for. */ skipToContent: 'Skip to content', }, /** * `chrome/ui/error-boundary.tsx` — what a preview says when the component inside * it threw. Short on purpose: the message and the stack go to the console, * and the point on screen is that the rest of the page still works. */ error: { title: 'This preview failed to render.', titleFor: (label: string) => `${label} failed to render.`, /** The compact form, inside a matrix cell. */ cell: 'Error', }, /** * The sidebar's own labels. Component names are not here — `Button` is the * import, not a word. */ nav: { /** Under the CBAR lockup; the lockup itself already names the institution. */ brandCaption: 'Design System', searchPlaceholder: 'Search components…', /** * Accessible name of the `/` hint in the search field. The glyph itself is * the key you press, so it is not translated — this is the sentence a screen * reader gets in its place. */ searchShortcut: 'Press slash to search', /** Under the field while it is filtering — with forty links behind it, how * much is left is the question the list itself cannot answer at a glance. */ matchCount: (n: number) => `${n} ${n === 1 ? 'component' : 'components'}`, overview: 'Overview', introduction: 'Introduction', tokens: 'Design tokens', icons: 'Icons', buttons: 'Buttons', alerts: 'Alerts', avatars: 'Avatars', badges: 'Badges', toasts: 'Toasts', parity: 'Figma parity', noMatch: 'No match.', open: 'Open navigation', /** The desktop pair. The rail is a column there, not a drawer, so it is * hidden and shown rather than opened and closed. */ hide: 'Hide navigation', show: 'Show navigation', /** Accessible name of the rail — and the drawer's title below `lg`. */ rail: 'Design system navigation', railDescription: 'Foundations and every component in the kit.', }, /** The header breadcrumb: the file, then where you are in it. */ header: { breadcrumb: 'Breadcrumb', root: 'Design System', components: 'Components', foundations: 'Foundations', notFound: 'Not found', }, notFound: { title: (path: string) => `Nothing at “${path}”`, body: 'Pick a component from the sidebar.', }, toolbar: { figma: 'Figma', dark: 'Dark', light: 'Light', bridge: { live: 'The bridge is up and the Figma plugin window is open.', noPlugin: 'The bridge is up but nothing is polling it. In Figma: Plugins → Development → “CBAR Figma bridge”.', off: 'No bridge on the port — every Figma panel reads the committed capture.', idle: 'Live Figma lookups are switched off for this session.', }, /** * The word in the pill beside the switch. Same four keys as `bridge` above, * which carries the sentence for the tooltip — a badge has room for one of * them and the explanation belongs on hover. */ status: { live: 'Live', noPlugin: 'No plugin', off: 'No bridge', idle: 'Off', /** Switched on, first probe still in flight. */ probing: 'Checking the bridge…', }, /** Prefixes the Figma file name in the indicator's tooltip. */ file: 'File', /** The axe switch beside it. Development only — see `chrome/hooks/use-a11y.ts`. */ a11y: 'Axe', a11yHint: 'Run axe-core against this page’s main region on every navigation. Development only — `color-contrast` is checked here, which the jsdom test suite cannot do.', }, /** `chrome/ui/a11y-panel.tsx`. */ a11y: { title: 'Accessibility', running: 'Auditing…', rerun: 'Run again', none: 'No violations in this page’s main region.', clean: 'Clean', count: (n: number) => `${n} ${n === 1 ? 'element' : 'elements'}`, reference: 'Rule reference', /** Footer, so a clean result is not read as "the whole page is clean". */ scope: 'Scoped to
. The site’s own chrome is not audited.', impact: { critical: 'Critical', serious: 'Serious', moderate: 'Moderate', minor: 'Minor', }, }, table: { props: 'Props', prop: 'Prop', type: 'Type', default: 'Default', description: 'Description', figmaSpec: 'Figma spec', /** Announced in place of the asterisk beside a required prop's name. */ required: 'required', }, matrix: { copyAll: (total: number) => `Copy all ${total} variants`, copyAllTitle: 'Every cell in this grid, under one import block', }, playground: { title: 'Playground', snippetTitle: 'Import line and JSX for this exact state', /** Names the control column beside the preview. */ controls: 'Props', /** * Clears every `p.*` param, which is where the playground's state lives — * so this is "put the URL back to the component's own defaults", and it is * hidden entirely when there is nothing to clear. */ reset: 'Reset', resetTitle: 'Back to this component’s default props', }, /** * `chrome/ui/share.tsx` — the playground's second button. "Copy code" hands out * source; these hand out the view, which is what a teammate who is not going * to paste anything actually asked for. */ share: { share: 'Share', copyLink: 'Copy link to this state', copyPreview: 'Copy preview link', /** The import line and the JSX — the same string the snippet block copies. */ copyCode: 'Copy component and import', openPreview: 'Open preview in a new tab', /** Replaces the plain "Copied" toast — a link, not a snippet, was copied. */ linkCopied: 'Link copied', }, /** `pages/embed.tsx` — the component alone, with no site around it. */ embed: { openInShowcase: 'Open in the showcase', notFound: 'No such component.', }, /** * `chrome/ui/tour.tsx` — the modal that opens once on a first visit and can be * reopened from the header afterwards. * * It overlaps `introduction` on purpose and is not a duplicate of it: that * page is reference prose you go to, this is six slides that come to you, * each one demonstrating the thing it describes with live components. Where * they say the same thing, say it shorter here. * * Nothing in the slides' demos is translated — they are real kit components * with real prop values, and `variant`, `solid` and `primary` are API * surface in every language. */ tour: { /** Toolbar button — tooltip text and accessible name both. */ reopen: 'Intro tour', /** * The overline above each slide's heading — the tour's own name, so the * dialog keeps one identity while the heading under it changes per slide. */ title: 'Introduction to the UI kit', step: (index: number, total: number) => `Step ${index} of ${total}`, back: 'Back', next: 'Next', /** The last slide's primary button. Not "Close" — it is a way in. */ done: 'Start exploring', skip: 'Skip', /** Accessible name of the progress bar, which shows no text of its own. */ progress: 'Tour progress', slides: { welcome: { title: 'Welcome to the design system', body: 'This site documents a UI kit that gets published as a package. Six slides on what is in it and what this site can do — about a minute.', components: (n: number) => `${n} components`, icons: (n: number) => `${n} icons`, subpaths: (n: number) => `${n} import paths`, }, components: { title: 'Components and their variants', body: 'Controls compose two independent axes: a fill treatment and a colour palette. Crossing them is why a handful of props covers CBAR’s whole canvas, and every component page draws that crossing as a grid.', /** Under the live grid, naming the axes it is made of. */ caption: 'Every control also takes a size, from xs to xl.', }, tokens: { title: 'Tokens, and one switch that proves it', body: 'No component names a colour. They name roles — background, ctl-solid, muted-foreground — and a token file maps those onto CBAR’s ramps. That indirection is the whole reason a rebrand is one file.', /** Beside the live theme switch inside the slide. */ tryIt: 'Press it — the site behind this dialog changes with it.', /** Accessible name of that switch. The sentence beside it is prose. */ switchLabel: 'Theme', dark: 'Dark', light: 'Light', }, icons: { title: 'The whole icon set', body: 'CBAR’s library ships with the kit, every icon drawn in outline and solid. They are tree-shaken one by one, so importing a single icon costs under a kilobyte.', /** Accessible name of the outline/solid switch. */ familyLabel: 'Icon family', outline: 'Outline', solid: 'Solid', }, site: { title: 'What this site can do', body: 'Every page is built from the same four parts:', playground: 'A playground with one control per prop, and its state lives in the URL.', matrix: 'Variant grids in the layout the Figma canvas uses.', copy: 'Copy anything — a cell, a grid, an example. What you copy is the element on screen, with its import lines.', parity: 'A parity report of what Figma draws against what the kit implements.', /** The `/` shortcut hint, printed beside a real . */ search: 'to search the sidebar from anywhere', }, start: { title: 'Start here', body: 'Install it, import the stylesheet once, then import components one at a time.', copyInstall: 'Copy the install command', links: 'Or read on:', }, }, }, /** * The `/introduction` page — ported from `docs/Introduction.mdx` when * Storybook became optional. Consumer-facing prose, so it is translated; * package names, subpaths and file names are not, being API surface. */ introduction: { title: 'UI Kit', lead: 'A component library that gets published, not an app. Every component here ships to your teammates as its own import path, with the CBAR design system baked into its tokens.', install: { title: 'Installing', body: 'The package carries its own compiled stylesheet, so a consuming app needs no Tailwind configuration at all.', copyInstall: 'Copy the install command', copyUsage: 'Copy the import lines', }, structure: { title: 'How it is put together', folders: { term: 'One folder per component', def: 'Implementation, test and an index.ts live together, and that index.ts is what creates the subpath — scripts/gen-exports.mjs reads the folder layout and writes package.json#exports from it. There is no hand-maintained list to forget.', }, tokens: { term: 'Two token layers', def: 'tokens.css holds raw values and is machine-owned; theme.css maps them onto roles and exposes those to Tailwind. Components may only ever name a role, so rebranding is one file — or a figma-sync run.', }, styles: { term: 'Styles ship compiled', def: 'The published package carries a styles.css built by the Tailwind CLI. Apps already running Tailwind v4 can import the token layers instead and let their own build produce the utilities.', }, deps: { term: 'Zero runtime dependencies', def: 'Installing the kit adds nothing to your lock file. Radix still carries the accessibility contract and tailwind-merge still makes your className win, but the first is compiled into dist and the second is vendored into src — so they are the kit’s problem, not yours. Icons, the variant builder, the class composer, the command palette, toasts and the date maths were kit code already.', }, }, usingSite: { title: 'Using this site', overview: (count: number) => `all ${count} components on one scroll, rendered live.`, tokens: 'every primitive and role read out of the running document, with CBAR’s own hex beside it.', icons: 'the whole icon set, searchable, with four copy formats per icon.', parity: 'what CBAR’s Figma file draws against what this kit implements.', toolbar: 'The theme and language switches live in the URL, so any view of this site is a link you can send to someone.', copy: 'Almost everything is copyable: a playground’s current state, a single matrix cell, a whole grid, an example. What you copy is the element on screen, with its import lines.', }, adding: { title: 'Adding a component', body: 'In Claude Code, one command scaffolds the folder, the test and the export:', copySkill: 'Copy the command', byHand: 'By hand: npx shadcn@latest add , move the generated file into its own folder, add an index.ts starting with the "use client" directive, then run pnpm exports:gen.', }, }, overview: { /* The `` is part of the sentence, not decoration, so its classes travel with the copy — the alternative is a placeholder syntax and a parser. */ lead: (count: number) => ( <> {count} component{count === 1 ? '' : 's'}, rendered live from{' '} src/ — not from a build. Every card links to a page with the full Figma variant matrix. ), }, component: { copyCode: 'Copy code', copyCodeTitle: 'Imports and JSX for this example', compositions: 'Compositions', }, tokens: { title: 'Design tokens', intro: ( <> tokens.css holds the primitives and is machine-owned — the figma-sync skill rewrites it wholesale.{' '} theme.css maps them to the semantic roles below, and components may only ever use those. ), hexNote: ( <> Each swatch shows the authored value and, under it, CBAR’s own hex — read from the comment the generator wrote beside the declaration, not converted from the oklch, so it is the number Figma prints. A role also names the primitive it resolves to, which the browser otherwise flattens away. A leading ≈ marks the few values with no Figma counterpart — an interpolated step, or a role with alpha — painted from the authored value instead. ), semanticRoles: 'Semantic roles', radius: 'Radius', radiusNote: 'Every radius derives from --ui-radius, so one edit re-rounds the whole kit. Components name the Tailwind alias (rounded-md), never the primitive.', elevation: 'Elevation', elevationNote: 'Two-layer shadows — a tight contact shadow plus a soft ambient one. A single blurred shadow reads as a grey smudge rather than as depth.', motion: 'Motion', motionNote: 'The one group components may name directly, along with the z-index scale: nothing re-maps duration or easing per theme, so there is no semantic layer above them. All motion is suppressed under prefers-reduced-motion by a global rule in globals.css, so no component has to handle it.', tokenColumn: 'Token', valueColumn: 'Value', syncTitle: 'Syncing from Figma', syncNote: 'tokens.css is machine-owned. With the Figma bridge running, the figma-sync skill reads the Variables, shows the diff and rewrites the primitives. theme.css — the mapping from primitives to roles — is hand-owned and survives the sync.', copyCss: 'Copy CSS', copyCssTitle: (section: string) => `Copy all ${section} tokens as CSS, hex values`, copyHexTitle: (hex: string) => `Copy ${hex}`, derivedNote: 'Approximate — no Figma hex exists for this value, so it was painted from the authored one. Either a step the kit interpolated past CBAR’s ramp, or a value with alpha that no ramp step defines.', }, /* Icon names, category names and the `variant`/`size` values stay as they are — they are what you type, not what you read. */ icons: { title: 'Icons', lead: (count: number, categories: number, pkg: string) => ( <> The whole CBAR set — {count} icons in {categories} categories, each with an outline and a solid drawing behind one variant prop. They ship from {pkg}/icons{' '} and are absent from the root barrel on purpose, so names like CheckIcon never collide with an app’s own set. Click one to copy it. ), searchPlaceholder: 'Search icons…', allCategories: 'All categories', variant: 'Variant', family: 'Family', size: 'Size', detailMeta: (category: string, shared: boolean, size: number) => ( <> CBAR {category} · {shared ? 'one drawing for both families' : 'outline + solid'}{' '} · {size}px ), sharedNote: ( <> CBAR draws this one once — variant is accepted for consistency but changes nothing. ), copyUsage: 'Copy import + JSX', copySvg: 'Copy SVG', copySvgTitle: 'The rendered markup, kit classes stripped', copyName: 'Copy name', copyComponent: 'Copy component', copyComponentTitle: 'A standalone React component — no dependency on this kit', empty: (query: string, category: string | null) => `Nothing matches “${query}”${category ? ` in ${category}` : ''}.`, }, /* The cells' own `Button` label and `aria-label="Search"` are not here: they are demo content, and `codegen.ts` serialises them into the snippet you copy. */ buttons: { title: 'Buttons', lead: (pkg: string) => ( <> Nine variants, seven palettes and nine sizes out of one component, shipping from{' '} {pkg}/button. Treatment and hue are separate props the way CBAR models them — five of the variants combine with any palette, four pin their own colour and ignore it. Every cell carries the icon on each side that CBAR’s own set draws by default; the{' '} Icons control takes them off. Hover a cell to copy it as JSX with its import lines — one cell at a time, which is the only unit anyone pastes. The filters live in the URL, so the view you are looking at is the link you send. ), searchPlaceholder: 'Search variants, palettes, sizes…', palette: 'Palette', size: 'Size', icons: 'Icons', disabled: 'Disabled', all: 'All', empty: (query: string) => ( <> Nothing matches “{query}”. Try a variant (outline), a palette (green ) or a size (icon-lg). ), composableTitle: 'Composable variants', composableLead: ( <> Five treatments against seven palettes at five heights. They cost twelve cva entries in total, because a variant reads the --ctl-* roles the palette class sets rather than naming a colour — which is why adding a palette costs nothing per variant. ), /* MatrixGrid prints a description as plain text, so these three carry no markup. */ composableGrid: (disabled: boolean) => `size × colorPalette${disabled ? ', disabled' : ''}`, pinnedTitle: 'Pinned-colour variants', pinnedDescription: 'These four paint from the semantic roles in theme.css, so they carry their own colour and ignore colorPalette — the palette filter above does not reach them. The link variant has no box at all, which is why its row reads as text rather than as a control.', iconOnlyTitle: 'Icon-only sizes', iconOnlyDescription: (palette: string) => `Square by size rather than by a separate component: 32 / 36 / 40 / 44px, drawn here at colorPalette="${palette}". The icon is a child, so a copied cell brings two import lines. Give every one of them an aria-label — there is no text to read.`, tabs: { label: 'View', gallery: 'Gallery', figma: 'Figma layout', }, /* The second tab. Axis values (solid, hover, xl…) stay English everywhere — they are prop values, and the snippets people copy contain them. */ figma: { lead: ( <> The same buttons in the shape CBAR’s canvas draws them: one band per palette, a column per treatment and a row per interaction state. Set Button ( 2446:6959) lays its four sizes out as blocks side by side, so the{' '} Size control below is which block you are looking at. Put it on{' '} All to get the canvas itself. ), stateNote: ( <> Only disabled is a prop here. hover and active are CSS states, so those two rows are forced with the very class{' '} controlVariants puts behind :hover — the row shows what the kit really paints, not an approximation. They share a row because CBAR draws them identically on all five treatments, and the kit has no :active treatment of its own. ), copyNote: ( <> What you copy is the button without that forced class: hover is a state, not a variant, so the snippet is the control and your pointer is the rest. ), swapNote: ( <> CBAR’s Button set paints this band the other way round — its{' '} colorPalette=primary is bound to surface-colored-secondary. The Figma variables, IconButton and ProgressCircle all agree with what is drawn here, so the set is treated as mis-wired. Full write-up in docs/FigmaParity.mdx. ), sizeNote: ( <> CBAR draws no sm rung for Button — it jumps xs →{' '} md. The kit fills it in at 36px, and it is on the Gallery tab. ), state: 'State', allSizes: 'All', /* MatrixGrid prints its description as plain text. */ grid: (palette: string, size: string) => `colorPalette="${palette}", size="${size}"`, }, }, /* The cells' own `AlertTitle` text is not here: it is demo content, and `codegen.ts` serialises it into the snippet you copy. */ alerts: { title: 'Alerts', lead: (pkg: string) => ( <> Four variants and seven palettes out of one component, shipping from{' '} {pkg}/alert. Unlike Button, none of them carry a hover state — an alert is not interactive. Hover a cell to copy it as JSX with its import line — one cell at a time, which is the only unit anyone pastes. The filters live in the URL, so the view you are looking at is the link you send. ), searchPlaceholder: 'Search variants, palettes, sizes…', palette: 'Palette', size: 'Size', all: 'All', empty: (query: string) => ( <> Nothing matches “{query}”. Try a variant (surface), a palette (tertiary) or a size (lg). ), /** MatrixGrid prints a description as plain text. */ grid: 'size × colorPalette', /** The demo `AlertTitle` every cell on both tabs carries. */ cellTitle: 'Heads up', /** The demo `AlertDescription` beside it — Figma-tab cells only. */ cellDescription: 'Every variant on the canvas carries one.', tabs: { label: 'View', gallery: 'Gallery', figma: 'Figma layout', }, /* The second tab. Axis values (solid, info, lg…) stay English everywhere — they are prop values, and the snippets people copy contain them. */ figma: { lead: ( <> The same alerts in the shape CBAR’s canvas draws them: one band per status, a row per treatment and a column per size. Set Alert (2446:316) draws no interaction states at all — every prop shown here is a real prop. Each cell is 421px wide and carries an icon, a title and a description, because all 72 variants on the canvas do and none of the three is behind a property. ), bandNote: ( <> CBAR splits treatment from meaning the same way the kit does: status is what the kit expresses as colorPalette. The pairing is read off the fills, not the names — info is the turquoise secondary ( #4BC7B5) and info-secondary the navy primary ( #004976), which is what the variable table says too. tertiary{' '} is the one kit palette with no band here; it is still on the Gallery tab, which sweeps every kit palette. ), sizeNote: ( <> CBAR calls the smallest rung default; the kit calls it sm so the ladder reads like every other component’s. The description steps with it — 12 / 16 / 18px — the way the set draws it. ), outlineNote: ( <> Two things here are deliberately not copied from the file: its{' '} outline variant carries a stray navy tint on warning,{' '} success and info-secondary but not on the other three, and it draws the same status icon on all six bands. See finding 7 on the{' '} /parity?tab=findings tab. ), status: 'Status', /* MatrixGrid prints its description as plain text. */ grid: (status: string, palette: string) => `status="${status}" (colorPalette="${palette}")`, }, }, /* The cells' own initials are not here — `CB` on the Gallery tab and the canvas's own `AX` on the Figma one. They are demo data, and `codegen.ts` serialises them into the snippet you copy. */ avatars: { title: 'Avatars', lead: (pkg: string) => ( <> Three treatments, seven palettes, six rungs and two shapes out of one component, shipping from {pkg}/avatar. shape is a third axis CBAR draws explicitly, so a grid here is pinned to one shape rather than trying to read as three dimensions. Hover a cell to copy it as JSX with its import line — one cell at a time, which is the only unit anyone pastes. The filters live in the URL, so the view you are looking at is the link you send. ), searchPlaceholder: 'Search variants, palettes, sizes, shapes…', palette: 'Palette', size: 'Size', shape: 'Shape', all: 'All', empty: (query: string) => ( <> Nothing matches “{query}”. Try a variant (outline), a palette ( tertiary), a size (2xs) or a shape (square). ), /** MatrixGrid prints a description as plain text. */ grid: 'size × colorPalette', tabs: { label: 'View', gallery: 'Gallery', figma: 'Figma layout', }, /* The second tab. Axis values (solid, square, 2xs…) stay English everywhere — they are prop values, and the snippets people copy contain them. */ figma: { lead: ( <> The same avatars in the shape CBAR’s canvas draws them: one band per palette, a block per treatment, shape down and size across. Set avatar ( 2446:757) draws no interaction states, so every prop shown here is a real prop. The blocks sit side by side and scroll rather than wrapping, because the ladder shrinking left to right is what the canvas is showing. ), bandNote: ( <> Two of the four bands are misnamed in the file. CBAR’s{' '} colorPalette=primary binds surface/colored/secondary ( #4BC7B5, turquoise) and its secondary binds{' '} surface/colored/primary (#004976, navy) — crossed, exactly as they are on the Button set, while the variable table says{' '} brand/primary/500 = #004976. Each heading names the kit palette that actually paints the band. third and neutral are plain renames and match; red, green and yellow have no band here and are still on the Gallery tab. ), sizeNote: ( <> Measured through the bridge, the ladder is 24 / 32 / 36 / 40 / 48 / 64px with type at 10 / 12 / 14 / 16 / 20 / 24px — every rung identical to what the kit draws. The square corner is radius/md at all six sizes rather than a scaling one, so a 24px avatar keeps the same 8px as a 64px one. ), swapNote: ( <> CBAR’s avatar set paints this band with the opposite role — the same crossing Button carries. The kit agrees with the variable table and is not changed for it; the heading names what actually paints it. Full write-up in finding 1 on the{' '} /parity?tab=findings tab. ), imageNote: ( <> The set’s fifth axis, avatar-type, is drawn six times only — at{' '} solid / square / third, once per rung. The kit expresses it as a child rather than as a prop (AvatarImage instead of{' '} AvatarFallback), which is why the parity report pins it to{' '} initials. The picture below is a local placeholder: the canvas fills these with a photograph, which is not ours to redistribute. ), palette: 'Palette', /* MatrixGrid prints its description as plain text. */ grid: 'shape × size', imageGrid: 'size', }, }, /* The cells' own text is not here — `Badge` on the Gallery tab and the canvas's own `Label` on the Figma one. They are demo data, and `codegen.ts` serialises them into the snippet you copy. */ badges: { title: 'Badges', lead: (pkg: string) => ( <> Five treatments against seven palettes, plus five shorthands that pin their own colour, shipping from {pkg}/badge. A badge is not interactive, so nothing here has a hover state unless asChild has made it a link. Hover a cell to copy it as JSX with its import line — one cell at a time, which is the only unit anyone pastes. The filters live in the URL, so the view you are looking at is the link you send. ), searchPlaceholder: 'Search variants, palettes, sizes…', palette: 'Palette', size: 'Size', icons: 'Icons', all: 'All', empty: (query: string) => ( <> Nothing matches “{query}”. Try a variant (surface), a palette ( tertiary) or a size (xs). ), /** MatrixGrid prints a description as plain text. */ grid: 'size × colorPalette', composableTitle: 'Composable treatments', composableLead: ( <> These five paint from the --ctl-* roles, so they take a colorPalette{' '} and there is a cell for every pairing. ), shorthandTitle: 'Shorthands', shorthandLead: ( <> These five pin their colour from the semantic roles instead, so they ignore{' '} colorPalette rather than racing it on specificity — which is why they are crossed with size here and not with the palette axis. Note{' '} variant="secondary" is the neutral grey role, not the turquoise{' '} colorPalette="secondary" ramp; the two names collide, and that is the price of keeping the old shorthands. ), /* MatrixGrid prints its description as plain text. */ shorthandGrid: 'colorPalette has no effect on these five', tabs: { label: 'View', gallery: 'Gallery', figma: 'Figma layout', }, /* The second tab. Axis values (solid, removed, xs…) stay English everywhere — they are prop values, and the snippets people copy contain them. */ figma: { lead: ( <> The same badges in the shape CBAR’s canvas draws them: one band per colour, a row per size and a column per treatment. Set Badge (2456:5146) declares 32 variants and all 32 are below — four bands of eight. It draws no interaction states, so every prop shown here is a real prop. ), bandNote: ( <> CBAR splits treatment from meaning the same way the kit does: color is what the kit expresses as colorPalette. The pairing is read off the fills, not the names — those name a role (removed, default) rather than a ramp, so there is nothing to match on. Each heading names the kit palette that actually paints the band. ), defaultNote: ( <> color="default" is the turquoise secondary, not the navy primary. Measured through the bridge it is #4BC7B5 solid /{' '} #CFF3EC surface / #A6E8DA border — secondary-500 /{' '} -100 / -200 exactly, the same three values the Alert set uses for its info band. That leaves primary, tertiary{' '} and yellow with no band here; all three are on the Gallery tab, which sweeps every kit palette. ), variantNote: ( <> The canvas draws two treatments. CBAR’s surface is the kit’s{' '} surface: all 16 of those variants carry{' '} box-shadow: 0 0 1px 0 <colour> inset, a 1px border drawn as an inset shadow, which is the tint-plus-outline the kit means by the name. The kit’s{' '} subtle, outline and ghost have no column here and are on the Gallery tab. ), iconNote: ( <> .iconStart? and .iconEnd? are booleans on the set and both default to true, so every variant on the canvas carries a glyph on each side — Outline/Interface/Plus and Outline/Interface/Settings, the set’s own swap defaults. The kit takes icons as children rather than as props, so the arrangement is child order and there is nothing to compare on the parity page; it is a control here instead, starting where Figma starts. Measured, the box, padding, gap, type and glyph match the kit at all four rungs. ), color: 'Colour', /* MatrixGrid prints its description as plain text. */ grid: (color: string, palette: string) => `color="${color}" (colorPalette="${palette}")`, }, }, /* The cells' own `Success` / `Description` are not here: they are the canvas's own demo copy, and they travel into the call people copy. */ toasts: { title: 'Toasts', lead: (pkg: string) => ( <> Five statuses out of one component, shipping from {pkg}/toast. Toast is the odd one out on this shelf: its status is not a prop. You do not render a toast — you call{' '} toast.success(…) and a store hands it to the one Toaster mounted near your app root. So the gallery below shows what each kind looks like and the calls that produce them. The filters live in the URL, so the view you are looking at is the link you send. ), searchPlaceholder: 'Search kinds…', closeButton: 'Close button', icons: 'Icons', all: 'All', dismissAll: 'Dismiss all', empty: (query: string) => ( <> Nothing matches “{query}”. Try a kind (warning, loading). ), kindsTitle: 'Every kind', kindsLead: ( <> Six kinds, against CBAR’s five states: loading has no counterpart on the canvas, and what the design file calls neutral is what the kit calls{' '} default. The colour comes entirely from the kind — it selects a palette, and the surface and the leading edge read --ctl-subtle and --ctl-border{' '} from it. ), /* MatrixGrid prints its description as plain text. */ kindsGrid: 'kind — the copy button gives you the call, not the markup', liveTitle: 'The real thing', liveLead: ( <> The grid above is drawn statically so all six can be compared at once. This is how a toast is actually raised. Nothing here is pinned, so every one expires on its own after four seconds — a pinned toast would follow you onto the next page, because the queue is a module singleton shared by the whole app. ), position: { title: 'Where it appears', lead: ( <> bottom-right is only the default. The corner is the one thing about a toast you do not choose when you raise it: it belongs to the Toaster, so every toast in an app shares it. Pick one below and the showcase’s own{' '} Toaster moves — the button raises a real toast, not a picture of one. ), legend: 'Toaster position', demo: 'Raise one here', /* English, like every other value the copied calls contain. */ demoBody: 'The Toaster owns the corner; the call does not.', note: ( <> Six values, and the stacking order follows: newest nearest the edge, so a top corner fills downwards and a bottom corner upwards. Mount exactly one Toaster — a second one does not split the toasts between two corners, it renders the whole queue twice. ), }, tabs: { label: 'View', gallery: 'Gallery', figma: 'Figma layout', }, /* The second tab. Axis values (success, neutral, info…) stay English everywhere — they are prop values, and the calls people copy contain them. */ figma: { lead: ( <> The same toasts in the shape CBAR’s canvas draws them: a single column, one row per state, top to bottom. Set Toast (2414:10758) declares five variants and all five are below. Each is 306px wide with 12px 16px of padding, a 6px radius and a 3px border down the leading edge only — not an outline. It draws no interaction states, so every prop shown here is a real prop. ), bandNote: ( <> No guesswork in the pairing for once: figma_css reports the bound variable names outright — --surface-subtle-secondary / --border-colored-secondary{' '} for info, --border-colored-black for neutral — and they already match kindPalettes in the component. So CBAR’s{' '} info is the turquoise secondary, the third set to say so after Alert’s info band and Badge’s default colour. ), neutralNote: ( <> Four of the five match to the byte. neutral does not, and the reason is in the design file: CBAR carries two variables for the same role —{' '} --surface-subtle-neutral (#E5E5E5), which Alert uses, and{' '} --surface-subtle-gray (#F9F9F9), which Toast and Badge use. The kit has one palette-black, so it agrees with Alert and reads a step lighter here. Re-pointing it would fix two sets by breaking the one that is right. ), iconNote: ( <> iconLeft? and iconRight? are booleans on the set and both default to true. The canvas puts the same glyph in the left slot on all five states — Solid/Status/Info-circle — and only recolours it, with the close cross taking the same colour beside it. The kit was changed to match, so this tab forces nothing: what you see is the component as any app using it renders a toast. ), inkNote: ( <> The ink is CBAR’s own, measured off the icon vectors and every one of them already a named ramp step — green-600 #16A34A, red-600{' '} #F00000, yellow-ink #865F28,{' '} secondary-500 #4BC7B5, neutral-800{' '} #222222 — which is what let the kit adopt them as a token,{' '} --ctl-icon, rather than as five literals. Worth knowing the cost: against their own tints they measure 3.00:1 on success, 3.65:1 on error{' '} and 1.74:1 on info, where a turquoise glyph sits on a turquoise surface. The title and description beside them stay on --ctl-fg at 6.18 / 6.86 / 8.34, so the text is unaffected — the glyph is decoration that repeats what the colour already says. See the /parity?tab=findings tab. ), closeNote: ( <> The canvas’s right slot is a close button, and it defaults on — so this tab draws it on too. The kit’s Toaster closeButton defaults off, which the Gallery tab shows instead. Flipping the kit default would put a button on every toast in every app already using it, so it stays a choice rather than becoming one. ), copyNote: ( <> Copying a cell gives you the toast.* call, not the markup — the surface on screen is drawn by an internal component the package does not publish, so there is no JSX worth pasting. This is the one place on this page where what you copy is not literally what you see, and it is the honest direction to relax the rule in. ), state: 'State', gridTitle: 'state', /* MatrixGrid prints its description as plain text. */ grid: 'state — canvas order, top to bottom', }, }, parity: { title: 'Figma parity', tabs: { /** The generated diff. */ report: 'Report', /** The written-up findings — what has been resolved, and by which side. */ findings: 'Findings', }, findingsEnglishOnly: 'This page is kept in English: it is an audit record of node ids, hex values and variable paths, aimed at whoever has the Figma file open.', lead: ( <> Every component set on CBAR’s canvas, diffed against the axes this kit implements. Captured with the local bridge plugin — figma_sets over the whole document, stored as{' '} showcase/src/registry/figma-spec.json . Re-capture it with pnpm figma:spec when the design file moves. ), stateNote: ( <> A gap is not automatically a bug. CBAR draws a state axis on most sets, and the kit renders those in CSS rather than as props — those rows are marked and excluded from the counts. ), slotsNote: ( <> The slots row is the one to read carefully. Those are the properties that are not variant axes — iconLeft?, .iconEnd?, .label? — which no variant name mentions. Button carries a left and a right icon toggle, both on by default, so its set draws four icon arrangements. The kit takes icons as children, so there is nothing to diff; this row is a checklist of what a component’s demo should actually show. ✓ / ✗ is CBAR’s default for a boolean,{' '} ⇄ marks a swappable instance, ✎ a text slot. ), stats: { sets: 'Figma sets', linked: 'Linked to a component', gaps: 'With an axis gap', kitOnly: 'Kit-only components', }, badge: { match: 'match', diff: 'differs', state: 'state axis', noProp: 'no prop', extra: 'kit only', }, values: { both: 'both', drawnInCss: 'drawn in CSS', onlyFigma: 'only in Figma', onlyKit: 'only in the kit', takenAsChildren: 'taken as children here', }, slots: 'slots', linkedSets: 'Linked sets', pageMeta: (page: string, variants: number) => `page “${page}” · ${variants} variants`, setMeta: (page: string, variants: number) => `${page} · ${variants} variants`, unlinkedTitle: (count: number) => `Figma sets with no component (${count})`, unlinkedNote: 'Either genuinely missing from the kit, or drawn in Figma as a piece of another component — a search field inside a Select, one page cell of a Pagination strip.', kitOnlyTitle: (count: number) => `Kit components with no Figma set (${count})`, kitOnlyNote: "Mostly the data-heavy widgets and the overlays CBAR never drew. They follow the kit's own conventions, so there is nothing to diff — but they are also the ones a design review will not catch.", }, /* The per-component Figma panel. Its `state` badge says something different from the parity page's — there the axis is the subject, here the rendering is — so the two badge sets are deliberately not shared. */ figma: { title: 'Figma', badge: { match: 'match', diff: 'differs', state: 'drawn in CSS', noProp: 'no prop', extra: 'kit only', slot: 'slot', }, columns: { figmaProperty: 'Figma property', type: 'Type', values: 'Values', figmaDefault: 'Figma default', kitProp: 'Kit prop', status: 'Status', property: 'Property', figma: 'Figma', kit: 'Kit', }, valueKinds: { boolean: 'on / off', text: 'free text', instance: 'swappable instance', }, children: 'children', missingHere: (values: string) => `missing here: ${values}`, kitAdds: (values: string) => `kit adds: ${values}`, status: { live: (file: string) => `live · ${file}`, noPlugin: 'bridge up · plugin window closed', notRunning: 'snapshot · bridge not running', snapshot: 'snapshot', }, onPage: (page: string) => `Figma is on page “${page}”`, setMissing: (set: string) => ( <> This entry points at the component set {set}, which is not in{' '} figma-spec.json. Re-run{' '} pnpm figma:spec after the design file moves. ), setSummary: (set: string, page: string, variants: number, props: number) => ( <> Component set {set} on page “{page}” — {variants}{' '} variants, {props} properties. Captured with the local bridge plugin; the live half below needs it running. ), matchingVariant: (label: string) => ( <> Matching variant {label} ), opening: 'Opening…', openInFigma: 'Open in Figma', unmatched: (values: string) => ( <> CBAR draws no {values} — the kit offers a rung the component set does not. Nothing below would be a like-for-like comparison, so it is left out. ), guessed: (values: string) => ( <> No prop decides {values} — Figma’s own default was used. Add a pin or{' '} valueMap to this entry’s figma link to choose deliberately. ), noVariant: 'The set has no variant with those values. CBAR does not draw every combination — check the properties table above for the axes that differ.', svgTooBig: (kb: number) => `That variant exports to ${kb} kB of SVG — too much to inline. Open it in Figma instead.`, loading: 'Loading…', computedStyle: 'Computed style', computedStyleNote: ( <> Figma’s getCSSAsync() against getComputedStyle on the element to the left. A blank cell means only one side states the property — Figma leaves out whatever the frame hugs. ), }, /** * Sidebar and breadcrumb headings, keyed by the English value `Entry.group` * carries. The registry itself stays English — it is data the pages read, not * copy — so this is the one place the nine values are turned into words. */ groups: { Actions: 'Actions', 'Data display': 'Data display', Feedback: 'Feedback', Forms: 'Forms', Navigation: 'Navigation', Disclosure: 'Disclosure', Overlays: 'Overlays', Data: 'Data', Layout: 'Layout', }, }; export type Messages = typeof en;