Foundations
Use in my project
Canon / Build with your agent

Canon

Conecte este design system ao seu agente de código no projeto. O Canon entrega as referências e instruções para o agente seguir nas próximas alterações de interface.

Conectar ao meu projeto
Workspace overview

A few everyday elements, with your design decisions applied.

Active

Lumen website redesign

Marketing site · Due Sep 19

12 tasks, 4 in review. Sofia Almeida owns the design track, Daniel Costa the build.

Updated 2h ago by Maya Chen
Active users
2,420
12%
vs last month

Live component samples · Use the theme toggle to compare light and dark.

Direction

Canon

Canon keeps a white canvas, neutral grey surfaces and familiar product layouts. Its character lives in the details: Vera’s orchid pink for actions, DM Sans, flat controls and compact corners. IBM Plex Mono labels technical details; surface and text color mark navigation selection.

  • Large surfaces stay white or neutral grey. Pink is a small action or selection detail; do not tint the page background.
  • Controls are 40px with 14px medium text and 6px corners. Use a flat fill or a single outline, without a bevel or resting shadow.
  • Cards have 10px corners and a hairline. Hover and selection strengthen the outline. Reserve elevation for floating layers.
  • DM Sans for reading and headings; IBM Plex Mono for compact section labels, IDs and code. Product headings are 22–34px, with short, useful copy.
  • Navigation distinguishes hover from selection: quiet hover, a persistent line or solid segment for the current view. Do not turn every label into a pill.
  • Featured icons use a simple outlined frame with a small corner cut; avoid concentric rings. Use one only when it helps identify the block.
  • Focus uses a visible 3px ring in both themes. Dark surfaces use lighter pink actions with dark ink labels.

Never

  • Generic "SaaS dashboard" layouts: a grid of identical cards with soft shadows and an icon in a colored circle.
  • Purple/blue gradients, glassmorphism, glowing borders, neon accents, floating blobs as decoration.
  • Raw hex/rgb/hsl colors, raw px sizes, or Tailwind palette classes (bg-blue-500) anywhere outside the token file.
  • Inventing a new component, variant, size or color when an existing one is listed in the spec.
  • Rounded-full pill buttons mixed with rounded-md buttons on the same screen; mixing radii.
  • Emoji as icons in product UI. Use the icon set defined in the spec.
  • Centered hero text with three feature cards below it as the default landing layout.
  • Placeholder copy such as "Lorem ipsum", "Feature 1", "Your company" in delivered UI.
  • More than one primary (filled action) button visible in the same view region.
  • Colored text for emphasis when weight, size or the kicker style would do.
  • Box-shadow on everything. Shadows are for floating layers; resting surfaces use a 1px hairline.
  • Inset bevels, concentric icon halos or purple as the default brand palette.
  • A hover state that looks selected when it is not.
  • Low-contrast labels on either theme; check the foreground against its actual surface.
Foundations

Color

Primitives

neutral
50#FAFAFA
100#F3F4F5
200#E5E7E9
300#CED2D6
400#9EA5AD
500#68727D
600#515C67
700#3F4A55
800#2F3942
900#232B33
950#151B21
brand
50#FEF8FC
100#FBF0F8
200#F6DEEF
300#EFC3E4
400#E899D6
500#DA65C4
600#B4309F
700#A22B8F
800#842374
900#68205C
950#43123A
red
50#FEF9F7
100#FCF1EE
200#F8E0DB
300#F2C7BF
400#EEA092
500#E37360
600#CA503E
700#B54737
800#8A3124
900#6D2A20
950#461812
amber
50#FCF9F7
100#F8F2ED
200#F0E4D9
300#E4CEBA
400#D6AF89
500#C48B50
600#A1641B
700#8F5815
800#744712
900#5C3A14
950#3A230A
green
50#F8FBF9
100#F0F5F1
200#DEEAE1
300#C3D9CA
400#9AC3A7
500#6BA981
600#488E64
700#397852
800#2C5F40
900#254C34
950#162F1F
blue
50#F8FAFD
100#EFF4F9
200#DDE7F3
300#C2D5E9
400#98BBE1
500#689DD3
600#4681BC
700#32679B
800#2A5580
900#244565
950#152A40

Semantic (use these, not primitives)

--cn-color-bg-canvas
#FFFFFF · dark #151B21
Page background. The outermost surface.
--cn-color-bg-surface
#FFFFFF · dark #232B33
Cards, panels, inputs, table rows. Sits on canvas.
--cn-color-bg-surface-raised
#FFFFFF · dark #2F3942
Popovers, dropdowns, dialogs. Floating above surface.
--cn-color-bg-subtle
#FAFAFA · dark #2F3942
Quiet fills: table headers, code, secondary chips, hover rows.
--cn-color-bg-muted
#F3F4F5 · dark #3F4A55
Stronger quiet fill: pressed states, skeletons, dividers-as-fill.
--cn-color-bg-inverse
#232B33 · dark #FAFAFA
Tooltips, inverted chips.
--cn-color-bg-overlay
rgba(21, 27, 33, 0.48) · dark rgba(8, 12, 16, 0.72)
Scrim behind dialogs and drawers.
--cn-color-bg-action
#B4309F · dark #EFC3E4
Primary action fill: primary buttons, active toggles, selected checkboxes.
--cn-color-bg-action-hover
#A22B8F · dark #F6DEEF
Primary action fill on hover.
--cn-color-bg-action-active
#842374 · dark #FBF0F8
Primary action fill while pressed.
--cn-color-bg-action-subtle
#F3F4F5 · dark #2F3942
Tinted fill for selected items, soft badges of the action color.
--cn-color-bg-accent
#B4309F · dark #E899D6
Brand identity fill (decorative highlights, "AI working" markers). Never text.
--cn-color-bg-accent-subtle
#F3F4F5 · dark #2F3942
Soft brand wash: chips, highlighted rows, callouts.
--cn-color-bg-success
#488E64 · dark #6BA981
Solid success fill.
--cn-color-bg-success-subtle
#F8FBF9 · dark rgba(2, 81, 32, 0.4)
Soft success fill (alerts, badges).
--cn-color-bg-warning
#C48B50 · dark #D6AF89
Solid warning fill.
--cn-color-bg-warning-subtle
#FCF9F7 · dark rgba(100, 52, 4, 0.4)
Soft warning fill.
--cn-color-bg-danger
#CA503E · dark #E37360
Solid danger fill: destructive buttons.
--cn-color-bg-danger-hover
#B54737 · dark #EEA092
Destructive button hover.
--cn-color-bg-danger-subtle
#FEF9F7 · dark rgba(122, 24, 21, 0.4)
Soft danger fill.
--cn-color-bg-info
#4681BC · dark #689DD3
Solid info fill.
--cn-color-bg-info-subtle
#F8FAFD · dark rgba(20, 59, 142, 0.4)
Soft info fill.
--cn-color-bg-disabled
#F3F4F5 · dark #2F3942
Fill of disabled controls.
--cn-color-fg-default
#232B33 · dark #FAFAFA
Primary text and icons.
--cn-color-fg-muted
#515C67 · dark #CED2D6
Secondary text: descriptions, helper text, table meta.
--cn-color-fg-subtle
#68727D · dark #9EA5AD
Tertiary text: timestamps, captions, kickers.
--cn-color-fg-placeholder
#68727D · dark #9EA5AD
Input placeholders.
--cn-color-fg-inverse
#FAFAFA · dark #151B21
Text on bg-inverse.
--cn-color-fg-on-action
#FFFFFF · dark #43123A
Text/icon on bg-action.
--cn-color-fg-on-accent
#FFFFFF · dark #43123A
Text/icon on bg-accent (rare).
--cn-color-fg-action
#A22B8F · dark #EFC3E4
Action-colored text: links in lists, selected tab labels, icons.
--cn-color-fg-accent
#A22B8F · dark #EFC3E4
Brand-colored text. AA on canvas; use sparingly.
--cn-color-fg-success
#397852 · dark #9AC3A7
Success text/icon.
--cn-color-fg-warning
#8F5815 · dark #D6AF89
Warning text/icon.
--cn-color-fg-danger
#B54737 · dark #EEA092
Danger text/icon, error messages.
--cn-color-fg-info
#32679B · dark #98BBE1
Info text/icon.
--cn-color-fg-disabled
#9EA5AD · dark #515C67
Text of disabled controls.
--cn-color-fg-link
#A22B8F · dark #EFC3E4
Inline links (underlined, ink-colored — never blue by default).
--cn-color-fg-link-hover
#842374 · dark #F6DEEF
Inline link on hover.
--cn-color-border-default
#E5E7E9 · dark #2F3942
Default 1px hairline: cards, inputs, dividers, table rules.
--cn-color-border-strong
#CED2D6 · dark #3F4A55
Hovered inputs, emphasized separators.
--cn-color-border-subtle
#F3F4F5 · dark rgba(69, 71, 72, 0.6)
Very quiet separators inside a surface.
--cn-color-border-action
#B4309F · dark #EFC3E4
Focused inputs, selected cards.
--cn-color-border-success
#C3D9CA · dark #2C5F40
--cn-color-border-warning
#E4CEBA · dark #744712
--cn-color-border-danger
#F2C7BF · dark #8A3124
Invalid inputs.
--cn-color-border-info
#C2D5E9 · dark #2A5580
--cn-color-border-disabled
#E5E7E9 · dark #2F3942
--cn-color-border-control
#CED2D6 · dark #515C67
Border of inputs, selects and outline/secondary buttons at rest.
--cn-color-border-control-hover
#68727D · dark #9EA5AD
Border of inputs and outline buttons on hover.
--cn-color-ring-focus
rgba(180, 48, 159, 0.28) · dark rgba(229, 169, 218, 0.4)
The 3px focus ring color. Only ever shown on :focus-visible.
--cn-color-ring-danger
rgba(249, 92, 81, 0.35) · dark rgba(255, 150, 138, 0.4)
Focus ring on invalid controls.
Foundations

Typography

Sans: "DM Sans", system-ui, -apple-system, "Segoe UI", sans-serif
Mono: "IBM Plex Mono", ui-monospace, "SF Mono", monospace

.cn-text-display-xl68px / 1 · 600 · -0.04emHero headline. One per page, marketing only.
A place for work to take shape
.cn-text-display-lg56px / 1.1 · 600 · -0.04emSection hero on marketing pages.
A place for work to take shape
.cn-text-display-md42px / 1.1 · 600 · -0.02emBig numbers (KPI hero), page-level titles on marketing.
A place for work to take shape
.cn-text-heading-xl34px / 1.1 · 600 · -0.02emh1 in the app: page title.
A place for work to take shape
.cn-text-heading-lg28px / 1.25 · 600 · -0.02emh2: section title inside a page.
A place for work to take shape
.cn-text-heading-md22px / 1.25 · 600 · -0.02emh3: card title, dialog title.
A place for work to take shape
.cn-text-heading-sm16px / 1.25 · 600 · -0.01emh4: subsection title, list group title.
A place for work to take shape
.cn-text-heading-xs14px / 1.25 · 600 · 0h5: small title inside a compact card or row.
A place for work to take shape
.cn-text-body-xl18px / 1.5 · 400 · -0.01emLead paragraphs under hero and section titles on marketing pages (20px).
Every decision leaves a trace. Bring the next task, the people involved and the context you need into one clear view.
.cn-text-body-lg16px / 1.6 · 400 · -0.01emLede paragraphs, long-form reading.
Every decision leaves a trace. Bring the next task, the people involved and the context you need into one clear view.
.cn-text-body-md14px / 1.5 · 400 · 0Default UI text. Inputs, table cells, descriptions.
Every decision leaves a trace. Bring the next task, the people involved and the context you need into one clear view.
.cn-text-body-sm13px / 1.5 · 400 · 0Secondary text, helper text, dense tables.
Every decision leaves a trace. Bring the next task, the people involved and the context you need into one clear view.
.cn-text-body-xs12px / 1.5 · 400 · 0Captions, timestamps, footnotes.
Every decision leaves a trace. Bring the next task, the people involved and the context you need into one clear view.
.cn-text-label-lg16px / 1 · 500 · -0.01emLarge button labels, tabs on marketing.
Save changes
.cn-text-label-md14px / 1 · 500 · 0Buttons, tabs, form labels, menu items.
Save changes
.cn-text-label-sm13px / 1 · 500 · 0Small buttons, badges, table headers.
Save changes
.cn-text-label-xs12px / 1 · 500 · 0Tiny badges, counters.
Save changes
.cn-text-kicker11px / 1.5 · 500 · 0.08em · uppercaseA small, tracked mono label for orientation. Use ink, with a short rule to introduce major sections.
Field notes / 01
.cn-text-code-md13px / 1.6 · 400 · 0Code blocks.
const total = items.reduce((a, b) => a + b, 0);
.cn-text-code-sm12px / 1.5 · 400 · 0Inline code, kbd, IDs, technical values in tables.
const total = items.reduce((a, b) => a + b, 0);
.cn-text-numeric-md14px / 1 · 500 · 0Tabular numbers in tables and stats (font-variant-numeric: tabular-nums).
1,234,567.89 · 42% · $12,400
Foundations

Spacing

space.0
0px
space.1
4px
space.2
8px
space.3
12px
space.4
16px
space.5
20px
space.6
24px
space.7
28px
space.8
32px
space.9
36px
space.10
40px
space.11
44px
space.12
48px
space.14
56px
space.16
64px
space.20
80px
space.24
96px
space.28
112px
space.32
128px
space.40
160px
space.48
192px
space.56
224px
space.64
256px
space.px
1px
space.0.5
2px
space.1.5
6px
space.2.5
10px
space.3.5
14px
Foundations

Radius

radius.none0px
radius.xs1.5px
radius.sm3px
radius.md4.5px
radius.lg6px
radius.xl9px
radius.2xl12px
radius.3xl18px
radius.full9999px
radius.control6px
radius.card10px
radius.panel8px
radius.overlay12px
Foundations

Shadow

shadow.xsResting controls are flat. Borders and surface color separate them.
shadow.smResting cards and selected segments do not float.
shadow.mdDropdowns, popovers, hovered interactive cards.
shadow.lgDialogs, drawers, command palette.
shadow.xlLarge floating layers: command palette, big popovers.
shadow.2xlHero cards and app screenshots on marketing pages. Never inside the app shell.
shadow.3xlDevice mockups on marketing pages.
shadow.controlFlat button fill, without an inset bevel.
shadow.focusFocus ring. Always paired with border-action on inputs.
shadow.focus-dangerFocus ring on invalid controls.
Foundations

Control sizes

control.xs32px
control.sm36px
control.md40px
control.lg44px
control.xl48px

Motion

duration.instant 0ms · duration.fast 140ms · duration.normal 200ms · duration.slow 280ms · duration.slower 400ms
easing.standard cubic-bezier(0.2, 0, 0, 1) · easing.out cubic-bezier(0.16, 1, 0.3, 1) · easing.in cubic-bezier(0.4, 0, 1, 1) · easing.linear linear

Typography

Kicker .cn-kickeredit

The signature eyebrow: mono, uppercase, letter-spaced, muted, with an optional 6×6 brand square before the text. It sits above a title and names the type of thing the title is.

Use: Above a page title, a card title or a section title ("Prospect", "Step 2 of 4", "Agent report"). Not for status (Badge), not for counts (Counter), not for form labels (Field). Never clickable: if it filters it is a Tag, if it acts it is a Button.

With mark
Prospect
HTML
<div class="cn-kicker" data-tone="accent" data-size="sm"><span class="cn-kicker__mark"></span>Prospect</div>
Text onlyNo mark for secondary eyebrows and inline uses.
Step 2 of 4 · Sourcing
HTML
<div class="cn-kicker" data-tone="accent" data-size="sm">Step 2 of 4 · Sourcing</div>
TonesOnly the mark changes color. The text is always muted.
Prospect
Archive
Verified
Needs review
Blocked
HTML
<div class="cn-kicker" data-tone="accent" data-size="sm"><span class="cn-kicker__mark"></span>Prospect</div> <div class="cn-kicker" data-tone="neutral" data-size="sm"><span class="cn-kicker__mark"></span>Archive</div> <div class="cn-kicker" data-tone="success" data-size="sm"><span class="cn-kicker__mark"></span>Verified</div> <div class="cn-kicker" data-tone="warning" data-size="sm"><span class="cn-kicker__mark"></span>Needs review</div> <div class="cn-kicker" data-tone="danger" data-size="sm"><span class="cn-kicker__mark"></span>Blocked</div>
Page-level (md)
Agent report
HTML
<div class="cn-kicker" data-tone="accent" data-size="md"><span class="cn-kicker__mark"></span>Agent report</div>

Recipes

Above a titleKicker names the type, the title names the thing. space.2 between them.
Prospect

Nakamura Trading Co.

Osaka, Japan · Importer of frozen seafood

HTML
<div><div class="cn-kicker" data-tone="accent" data-size="sm"><span class="cn-kicker__mark"></span>Prospect</div><h2 class="cn-text-heading-lg" style="margin-top:var(--cn-space-2)">Nakamura Trading Co.</h2><p class="cn-text-body-sm" style="color:var(--cn-color-fg-muted);margin-top:var(--cn-space-1)">Osaka, Japan · Importer of frozen seafood</p></div>

Matrix

tone ↓ / size →smmd
neutral
Prospect
Prospect
accent
Prospect
Prospect
success
Prospect
Prospect
warning
Prospect
Prospect
danger
Prospect
Prospect
Anatomy, props, states, rules

Anatomy

  • .cn-kicker <div> — Inline-flex row carrying the kicker type style and the muted color. <div> above block titles, <span> when inline.
  • .cn-kicker__mark <span> · optional — Optional 6×6px square before the text, radius 1.5px, filled with the tone color (brand accent by default). Empty, decorative.

Props

propvalues (default outlined)meaning
marknone square dotnone = plain text (section subheading); square = 6px brand square before the text (Vero family signature); dot = round marker.
toneneutral accent success warning dangerColor of the mark only; the text always stays fg-subtle. accent = the brand square (default: page and card titles); neutral = grey square for secondary sections; success/warning/danger = the section is about a state ("Verified", "Needs review", "Blocked").
sizesm mdsm = 11px, the canonical kicker above card titles and inside tables (equals the kicker type style, so markup without data-size renders sm); md = 12px for page-level eyebrows and marketing sections.

Rules

  • One kicker per title, directly above it, with space.2 between them. Never two kickers stacked.
  • The kicker names the TYPE of thing; the title names the thing: "Prospect / Nakamura Trading Co.", never "Nakamura / Prospect".
  • 1–3 words. Write the source in sentence case ("Needs review"); the CSS uppercases it.
  • The text is always fg-subtle. Never color the text; tone colors only the mark.
  • Never a badge: no background, no border, no pill. Never clickable.
  • The mark goes only on the primary kicker of a surface (page title, card title). Inline and table uses go without the mark.
  • Do not use the kicker as a form label; Field labels are label-md in sentence case.
  • Column headers, chart legends and stat labels already speak in this voice through their own parts; do not nest a Kicker inside them.

Accessibility

  • The mark is an empty decorative span; screen readers skip it. Do not put text or an icon inside it.
  • The kicker is plain text, not a heading: no heading role, no aria-level. It is associated with the title by reading order.
  • Uppercasing is CSS, so assistive tech reads the source case ("Prospect", not letter by letter).
  • fg-subtle at 11px medium mono meets AA on canvas and surface; do not place kickers on tinted or dark backgrounds.
Typography

Prose .cn-proseedit

Container for long-form text (help articles, agent reports, release notes, legal): sets the reading measure and styles every HTML element inside it — headings, paragraphs, lists, quotes, code, tables — from the type scale, so markdown output looks right without classes.

Use: Wrap any block of rendered markdown or editorial HTML in it. Not for UI text (labels, descriptions, table cells), which uses the type utilities directly, and not for forms.

Article (md)

How agents verify a company

Before a company reaches your shortlist, an agent checks it against three public sources: customs records, the national business registry and the company’s own site. Only companies with a match in at least two sources are marked verified.

  • Customs records show real import volume and product codes (HS 0306 for frozen shrimp).
  • The registry confirms the legal entity, its age and its directors.
  • The website confirms the product line and the contact channel.

“Verified does not mean interested. It means the company exists, imports what you sell, and can be reached.”

Each check leaves a trace. Open a prospect and look for the verified_by field; it lists the sources and the date of the last check.

SourceWhat it provesRefreshed
Customs recordsImport volume, HS codesMonthly
Business registryLegal entity, directorsQuarterly
Company websiteProduct line, contactsWeekly

When a check fails

The prospect stays in the results as unverified and the agent retries after 7 days. You can force a re-check from the prospect drawer.

GET /v1/prospects/nk-2041/verification
→ { "verified": true, "sources": ["customs", "registry"], "checked_at": "2026-09-10" }
HTML
<article class="cn-prose" data-size="md"><h2>How agents verify a company</h2><p>Before a company reaches your shortlist, an agent checks it against three public sources: customs records, the national business registry and the company’s own site. Only companies with a match in at least two sources are marked <strong>verified</strong>.</p><ul><li>Customs records show real import volume and product codes (HS 0306 for frozen shrimp).</li><li>The registry confirms the legal entity, its age and its directors.</li><li>The website confirms the product line and the contact channel.</li></ul><blockquote><p>“Verified does not mean interested. It means the company exists, imports what you sell, and can be reached.”</p></blockquote><p>Each check leaves a trace. Open a prospect and look for the <code>verified_by</code> field; it lists the sources and the date of the last check.</p><table><thead><tr><th>Source</th><th>What it proves</th><th>Refreshed</th></tr></thead><tbody><tr><td>Customs records</td><td>Import volume, HS codes</td><td>Monthly</td></tr><tr><td>Business registry</td><td>Legal entity, directors</td><td>Quarterly</td></tr><tr><td>Company website</td><td>Product line, contacts</td><td>Weekly</td></tr></tbody></table><h3>When a check fails</h3><p>The prospect stays in the results as <em>unverified</em> and the agent retries after 7 days. You can force a re-check from the prospect drawer.</p><pre><code>GET /v1/prospects/nk-2041/verification
→ { "verified": true, "sources": ["customs", "registry"], "checked_at": "2026-09-10" }</code></pre></article>
Small (inside a drawer)

Release notes · 11 Sep 2026

Agents now read replies in Japanese and German and draft answers in the buyer’s language.

  1. Open a conversation with a reply in another language.
  2. Check the draft; the original is shown below it.
  3. Approve, edit, or ask the agent to rewrite.

Questions? See Working with drafts.

HTML
<article class="cn-prose" data-size="sm" style="max-width:360px"><h3>Release notes · 11 Sep 2026</h3><p>Agents now read replies in Japanese and German and draft answers in the buyer’s language.</p><ol><li>Open a conversation with a reply in another language.</li><li>Check the draft; the original is shown below it.</li><li>Approve, edit, or ask the agent to rewrite.</li></ol><p>Questions? See <a href="#">Working with drafts</a>.</p></article>
Large (marketing)

Find real buyers, not lists

Vera reads trade data the way an analyst does: who imported what, from where, how often. Then it writes to the people who can say yes — and waits for your approval before anything is sent.


Start with one product and one country. Most teams see the first verified reply within two weeks.

HTML
<article class="cn-prose" data-size="lg"><h2>Find real buyers, not lists</h2><p>Vera reads trade data the way an analyst does: who imported what, from where, how often. Then it writes to the people who can say yes — and waits for your approval before anything is sent.</p><hr><p>Start with one product and one country. Most teams see the first verified reply within two weeks.</p></article>

Matrix

size
sm

How agents verify a company

Before a company reaches your shortlist, an agent checks it against three public sources: customs records, the national business registry and the company’s own site. Only companies with a match in at least two sources are marked verified.

  • Customs records show real import volume and product codes (HS 0306 for frozen shrimp).
  • The registry confirms the legal entity, its age and its directors.
  • The website confirms the product line and the contact channel.

“Verified does not mean interested. It means the company exists, imports what you sell, and can be reached.”

Each check leaves a trace. Open a prospect and look for the verified_by field; it lists the sources and the date of the last check.

SourceWhat it provesRefreshed
Customs recordsImport volume, HS codesMonthly
Business registryLegal entity, directorsQuarterly
Company websiteProduct line, contactsWeekly

When a check fails

The prospect stays in the results as unverified and the agent retries after 7 days. You can force a re-check from the prospect drawer.

GET /v1/prospects/nk-2041/verification
→ { "verified": true, "sources": ["customs", "registry"], "checked_at": "2026-09-10" }
md

How agents verify a company

Before a company reaches your shortlist, an agent checks it against three public sources: customs records, the national business registry and the company’s own site. Only companies with a match in at least two sources are marked verified.

  • Customs records show real import volume and product codes (HS 0306 for frozen shrimp).
  • The registry confirms the legal entity, its age and its directors.
  • The website confirms the product line and the contact channel.

“Verified does not mean interested. It means the company exists, imports what you sell, and can be reached.”

Each check leaves a trace. Open a prospect and look for the verified_by field; it lists the sources and the date of the last check.

SourceWhat it provesRefreshed
Customs recordsImport volume, HS codesMonthly
Business registryLegal entity, directorsQuarterly
Company websiteProduct line, contactsWeekly

When a check fails

The prospect stays in the results as unverified and the agent retries after 7 days. You can force a re-check from the prospect drawer.

GET /v1/prospects/nk-2041/verification
→ { "verified": true, "sources": ["customs", "registry"], "checked_at": "2026-09-10" }
lg

How agents verify a company

Before a company reaches your shortlist, an agent checks it against three public sources: customs records, the national business registry and the company’s own site. Only companies with a match in at least two sources are marked verified.

  • Customs records show real import volume and product codes (HS 0306 for frozen shrimp).
  • The registry confirms the legal entity, its age and its directors.
  • The website confirms the product line and the contact channel.

“Verified does not mean interested. It means the company exists, imports what you sell, and can be reached.”

Each check leaves a trace. Open a prospect and look for the verified_by field; it lists the sources and the date of the last check.

SourceWhat it provesRefreshed
Customs recordsImport volume, HS codesMonthly
Business registryLegal entity, directorsQuarterly
Company websiteProduct line, contactsWeekly

When a check fails

The prospect stays in the results as unverified and the agent retries after 7 days. You can force a re-check from the prospect drawer.

GET /v1/prospects/nk-2041/verification
→ { "verified": true, "sources": ["customs", "registry"], "checked_at": "2026-09-10" }
Anatomy, props, states, rules

Anatomy

  • .cn-prose <article> — The container. Max width 65ch, default text color, base font from size. Every element inside is styled by tag; the only class you may add inside is .cn-kicker above a heading.

Props

propvalues (default outlined)meaning
sizesm md lgBase text size. sm = body-sm with headings one step smaller (side panels, drawers, tooltips of an article); md = body-md, the app default (help center, reports); lg = body-lg for marketing and long reads.

Rules

  • Prose is for rendered documents; UI text never goes inside it. If an element needs a class, it does not belong in Prose (except .cn-kicker above a heading).
  • One h1 per document; start articles inside the app at h2 because the page already has the h1.
  • Keep the measure: never widen beyond 65ch. Wide layouts put Prose in one column and media or a table of contents in the other.
  • Links are ink-colored and underlined, never blue. External links get the external icon after the text.
  • Code inline for identifiers and values; pre for anything with more than one line. Never pre for a single command inside a sentence.
  • Tables inside Prose are simple (≤ 4 columns); anything sortable or wider is a Table component outside the Prose.
  • Images and video inside Prose get radius lg and a caption in the figcaption, not in the paragraph below.
  • Size follows the container: sm in drawers and popovers, md in app pages, lg only on marketing and long-read pages.

Accessibility

  • Use semantic elements (h2–h4 in order, ul/ol, blockquote, table with th) so the structure is navigable by headings and landmarks.
  • The root is an <article> (self-contained) or a <section> with aria-labelledby when it is part of a page.
  • Link text says where it goes ("Working with drafts"), never "click here"; underline is kept so color is not the only cue.
  • Code blocks scroll horizontally and are focusable (tabindex="0") when they overflow, so keyboard users can reach the content.
Typography

Code .cn-codeedit

Monospace code, inline or as a block. Inline is a quiet chip in running text (gray-50, 1px gray-200 ring, radius 4); block is a card surface (white, hairline ring, shadow-xs, radius 12) with an optional file-name header and a copy button.

Use: Inline for literals inside a sentence: commands, file names, attribute values, token names. Block for multi-line snippets, config files and terminal output. Keyboard shortcuts are Kbd, not Code; numbers in tables use numeric-md, not Code.

Inline12px mono on gray-50 with a gray-200 ring, radius 4.
npm install -g @lumen/canon
HTML
<code class="cn-code" data-variant="inline" data-size="md">npm install -g @lumen/canon</code>
Inline in a sentence

Run canon build after editing design/tokens.json; the gallery at dist/preview.html reloads on its own.

HTML
<p class="cn-text-body-md" style="max-width:560px">Run <code class="cn-code" data-variant="inline" data-size="md">canon build</code> after editing <code class="cn-code" data-variant="inline" data-size="md">design/tokens.json</code>; the gallery at <code class="cn-code" data-variant="inline" data-size="md">dist/preview.html</code> reloads on its own.</p>
Block with file name and copyA card surface with a gray-50 header (12px semibold gray-500) and a 28px copy button.
design/system.json
{
  "name": "Lumen",
  "prefix": "ui",
  "brand": "#B4309F",
  "radiusScale": 1,
  "controlHeight": 40
}
HTML
<figure class="cn-code" data-variant="block" data-size="md" style="max-width:560px"><div class="cn-code__header"><span>design/system.json</span><button type="button" class="cn-code__copy" aria-label="Copy code"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg></button></div><pre class="cn-code__pre"><code>{
  "name": "Lumen",
  "prefix": "ui",
  "brand": "#B4309F",
  "radiusScale": 1,
  "controlHeight": 40
}</code></pre></figure>
Block, terminal (no header)
$ canon init "Lumen" --prefix ui
✓ built 86 files → design/dist
$ canon install
✓ DESIGN.md, CLAUDE.md block and lint hook installed
HTML
<figure class="cn-code" data-variant="block" data-size="md" style="max-width:560px"><pre class="cn-code__pre"><code>$ canon init "Lumen" --prefix ui
✓ built 86 files → design/dist
$ canon install
✓ DESIGN.md, CLAUDE.md block and lint hook installed</code></pre></figure>
Block, small12px mono with 12px padding for tables and popovers.
components.css
.ui-button[data-variant="primary"] {
  background-color: var(--ui-color-bg-action);
  color: var(--ui-color-fg-on-action);
}
HTML
<figure class="cn-code" data-variant="block" data-size="sm" style="max-width:480px"><div class="cn-code__header"><span>components.css</span><button type="button" class="cn-code__copy" aria-label="Copy code"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg></button></div><pre class="cn-code__pre"><code>.ui-button[data-variant="primary"] {
  background-color: var(--ui-color-bg-action);
  color: var(--ui-color-fg-on-action);
}</code></pre></figure>
Inline, small
Token {color.bg-action} uses the action color defined for the current theme.
HTML
<span class="cn-text-body-sm" style="color:var(--cn-color-fg-muted)">Token <code class="cn-code" data-variant="inline" data-size="sm">{color.bg-action}</code> uses the action color defined for the current theme.</span>

Matrix

variant ↓ / size →smmd
inlinenpm install -g @lumen/canonnpm install -g @lumen/canon
blocknpm install -g @lumen/canonnpm install -g @lumen/canon
Anatomy, props, states, rules

Anatomy

  • .cn-code <code> — Inline: a <code> span. Block: a <figure> wrapping the header and the pre.
  • .cn-code__header <div> · optional — Optional bar above the code: file path or language in the table-head voice (12px semibold gray-500) keeping its case, since file paths are case-sensitive. Copy button on the right, bottom hairline, gray-50 fill, 44px tall.
  • .cn-code__pre <pre> — The code in <pre><code>. 14px mono (12px on sm), 16px padding, scrolls horizontally, never wraps.
  • .cn-code__copy <button> · optional — 28px utility icon button (radius 6, 16px icon) in the header that copies the raw text. aria-label="Copy code".

Props

propvalues (default outlined)meaning
variantinline blockinline = chip inside text for a literal; block = full-width card surface with radius 12 for multi-line code, with optional header and copy button.
sizesm mdmd = 12px inline and 14px block for docs and settings; sm = 11px inline and 12px block for tables, tooltips and popovers.

Rules

  • Inline code is for literals the reader might type or see verbatim: commands, file names, attribute values, token names. Never for emphasis.
  • Block code never wraps; it scrolls horizontally. Keep lines under 80 characters in docs and trim leading indentation.
  • The header shows the file path or the language in its original case (design/system.json, not DESIGN/SYSTEM.JSON). Omit it for terminal commands and one-liners.
  • One copy button per block, in the header, 28px utility. It copies the raw text (not the rendered HTML) and confirms with a short "Copied" tooltip.
  • Escape HTML inside <code> (&lt; &gt; &amp;); never inject raw user content.
  • md in docs, settings and dialogs; sm inside tables, tooltips and popovers. Code does not scale with the surrounding heading.
  • Syntax highlighting, if any, uses fg-default, fg-muted, fg-accent and fg-info only. No rainbow palettes, no background per token.

Accessibility

  • Use the semantic elements: <code> inline and <pre><code> for blocks, so screen readers announce code.
  • The copy button has aria-label="Copy code" and a visible focus ring; announce "Copied" in a live region after the click.
  • When a block overflows horizontally, give the <pre> tabindex="0" so keyboard users can scroll it.
  • Never convey meaning by color alone in highlighted code; keep textual markers (+ / − in diffs).
Typography

Kbd .cn-kbdedit

A keyboard key drawn as the reference shortcut chip: 12px medium text on white with a 1px gray-300 ring, radius 4, 20px tall. Several in a row spell a shortcut (⌘ K).

Use: Show keyboard shortcuts in menus, tooltips, the command palette, input trailers and onboarding hints. Not for code (Code) and never as a button: a Kbd is not clickable.

Single key20×20 minimum, 12px medium gray-600, 1px gray-300 ring, radius 4.
HTML
<kbd class="cn-kbd" data-size="md">⌘</kbd>
ChordModifiers first, then the key, 4px between them.
K
HTML
<kbd class="cn-kbd" data-size="md">⌘</kbd><kbd class="cn-kbd" data-size="md">K</kbd>
Named keysWords stretch the chip; the padding stays 1px 4px.
EscPEnterTab
HTML
<span style="display:inline-flex;gap:var(--cn-space-4);align-items:center"><kbd class="cn-kbd" data-size="md">Esc</kbd><span><kbd class="cn-kbd" data-size="md">⇧</kbd><kbd class="cn-kbd" data-size="md">⌘</kbd><kbd class="cn-kbd" data-size="md">P</kbd></span><kbd class="cn-kbd" data-size="md">Enter</kbd><kbd class="cn-kbd" data-size="md">Tab</kbd></span>
In a sentence

Press K to open the command palette, / to search projects, or Esc to close any panel.

HTML
<p class="cn-text-body-md" style="max-width:560px">Press <kbd class="cn-kbd" data-size="md">⌘</kbd><kbd class="cn-kbd" data-size="md">K</kbd> to open the command palette, <kbd class="cn-kbd" data-size="md">/</kbd> to search projects, or <kbd class="cn-kbd" data-size="md">Esc</kbd> to close any panel.</p>
Small in a hint18px tall with 11px text for tooltips and table cells.
Save S
HTML
<span class="cn-text-body-sm" style="color:var(--cn-color-fg-muted)">Save <kbd class="cn-kbd" data-size="sm">⌘</kbd><kbd class="cn-kbd" data-size="sm">S</kbd></span>

Recipes

Menu item with shortcutShortcut right-aligned, size sm; the label stays the primary text.
DuplicateD
ArchiveE
Delete
HTML
<div style="width:240px;display:flex;flex-direction:column;gap:var(--cn-space-0-5)"><div class="cn-text-body-md" style="display:flex;align-items:center;justify-content:space-between;gap:var(--cn-space-6);height:36px;padding:0 var(--cn-space-2-5);border-radius:var(--cn-radius-md);background:var(--cn-color-bg-subtle)"><span>Duplicate</span><span><kbd class="cn-kbd" data-size="sm">⌘</kbd><kbd class="cn-kbd" data-size="sm">D</kbd></span></div><div class="cn-text-body-md" style="display:flex;align-items:center;justify-content:space-between;gap:var(--cn-space-6);height:36px;padding:0 var(--cn-space-2-5);border-radius:var(--cn-radius-md);"><span>Archive</span><span><kbd class="cn-kbd" data-size="sm">⌘</kbd><kbd class="cn-kbd" data-size="sm">E</kbd></span></div><div class="cn-text-body-md" style="display:flex;align-items:center;justify-content:space-between;gap:var(--cn-space-6);height:36px;padding:0 var(--cn-space-2-5);border-radius:var(--cn-radius-md);"><span>Delete</span><span><kbd class="cn-kbd" data-size="sm">⌘</kbd><kbd class="cn-kbd" data-size="sm">⌫</kbd></span></div></div>

Matrix

size
sm
md
Anatomy, props, states, rules

Anatomy

  • .cn-kbd <kbd> — One key: a symbol (⌘ ⇧ ⌥) or a short word (Esc, Enter, K). One Kbd per key; put them side by side for a chord.

Props

propvalues (default outlined)meaning
sizesm mdmd = 20px tall with 12px text, for menus, inputs, docs and the command palette (the reference chip); sm = 18px tall with 11px text inside tooltips and table cells.

Rules

  • One key per Kbd. A shortcut is a row of Kbds 4px apart, in the order pressed: modifiers first (⌃ ⌥ ⇧ ⌘), then the key.
  • Use platform symbols on macOS (⌘ ⌥ ⇧ ⌃ ⏎ ⌫ ⎋) and words on Windows/Linux (Ctrl, Alt, Shift, Enter). Detect the platform; never show both.
  • Letters are uppercase (K, not k); named keys are capitalized words (Esc, Tab, Space, Enter), never abbreviations like "Ret".
  • Kbd sits at the end of a menu item, tooltip, palette row or input, right-aligned. It never appears inside a button label.
  • Never use Kbd for pointer input (clicks, gestures) or for code; that is Code.
  • md by default; sm where the surrounding text is 12px (tooltips, table cells, dense menu rows).

Accessibility

  • Use the <kbd> element; screen readers read its text, so add aria-label on symbol keys ("Command", "Shift", "Escape").
  • A shortcut hint is supplementary: the same action must be reachable by pointer and through the menu.
  • Keep gray-600 on white; at 11–12px medium it meets AA. Do not lighten Kbd text to gray-500.
Actions

Button .cn-buttonedit

Triggers an action. Eleven intents share one shape (the system button): flat filled primary with a clear pressed state, ringed secondaries, transparent tertiaries and link styles, each in gray, brand and destructive voices. The intent is a data attribute, never a different component.

Use: Use for actions (save, send, open a dialog). Use Link for navigation inside text. One primary button per view region; put it last (right) in a row of actions. Destructive actions use the danger* variants and always confirm.

PrimaryFlat action fill with a contrasting label. One primary action per view region.
HTML
<button type="button" class="cn-button" data-variant="primary" data-size="md"><span class="cn-button__label">Save changes</span></button>
Gray and brand hierarchyoutline = Secondary gray, secondary-color = Secondary color, ghost = Tertiary, link = Link gray, link-color = Link color.
HTML
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-3)"><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Save changes</span></button><button type="button" class="cn-button" data-variant="secondary-color" data-size="md"><span class="cn-button__label">Save changes</span></button><button type="button" class="cn-button" data-variant="ghost" data-size="md"><span class="cn-button__label">Save changes</span></button><button type="button" class="cn-button" data-variant="link" data-size="md"><span class="cn-button__label">Save changes</span></button><button type="button" class="cn-button" data-variant="link-color" data-size="md"><span class="cn-button__label">Save changes</span></button></div>
Secondary (subtle fill)The secondary hover fill at rest. For dense toolbars where a white button would disappear.
HTML
<button type="button" class="cn-button" data-variant="secondary" data-size="md"><span class="cn-button__label">Save changes</span></button>
DestructivePrimary, secondary, tertiary and link destructive. Every one of them confirms before acting.
HTML
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-3)"><button type="button" class="cn-button" data-variant="danger" data-size="md"><span class="cn-button__label">Delete</span></button><button type="button" class="cn-button" data-variant="danger-outline" data-size="md"><span class="cn-button__label">Delete</span></button><button type="button" class="cn-button" data-variant="danger-ghost" data-size="md"><span class="cn-button__label">Delete</span></button><button type="button" class="cn-button" data-variant="danger-link" data-size="md"><span class="cn-button__label">Delete</span></button></div>
Sizes32 / 36 / 40 / 44 / 48px. Text stays 14px up to md and becomes 16px on lg and xl; icons are 20px (16px on xs).
HTML
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-3)"><button type="button" class="cn-button" data-variant="outline" data-size="xs"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg><span class="cn-button__label">Save changes</span></button><button type="button" class="cn-button" data-variant="outline" data-size="sm"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg><span class="cn-button__label">Save changes</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg><span class="cn-button__label">Save changes</span></button><button type="button" class="cn-button" data-variant="outline" data-size="lg"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg><span class="cn-button__label">Save changes</span></button><button type="button" class="cn-button" data-variant="outline" data-size="xl"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg><span class="cn-button__label">Save changes</span></button></div>
With icons and icon-onlyLeading icon = what the action is; trailing = where it goes. data-icon-only squares the padding (40×40 on md).
HTML
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-3)"><button type="button" class="cn-button" data-variant="primary" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg><span class="cn-button__label">Save changes</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Save changes</span><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg></button><button type="button" class="cn-button" data-variant="outline" data-size="md" data-icon-only aria-label="Copy link"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-button__label"><span class="cn-sr-only">Copy link</span></span></button><button type="button" class="cn-button" data-variant="ghost" data-size="md" data-icon-only aria-label="More"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.25"/><circle cx="8" cy="8" r="1.25"/><circle cx="12.5" cy="8" r="1.25"/></svg><span class="cn-button__label"><span class="cn-sr-only">More</span></span></button></div>
LoadingThe fill switches to the hover color, the label hides but keeps its width.
HTML
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-3)"><button type="button" class="cn-button" data-variant="primary" data-size="md" data-loading aria-busy="true"><span class="cn-button__spinner" aria-hidden="true"></span><span class="cn-button__label">Saving…</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md" data-loading aria-busy="true"><span class="cn-button__spinner" aria-hidden="true"></span><span class="cn-button__label">Exporting…</span></button></div>
DisabledWhole button at 50% opacity, cursor not-allowed.
HTML
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-3)"><button type="button" class="cn-button" data-variant="primary" data-size="md" disabled><span class="cn-button__label">Save changes</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md" disabled><span class="cn-button__label">Save changes</span></button><button type="button" class="cn-button" data-variant="ghost" data-size="md" disabled><span class="cn-button__label">Save changes</span></button><button type="button" class="cn-button" data-variant="danger" data-size="md" disabled><span class="cn-button__label">Delete</span></button></div>

Recipes

Action row (dialog footer)Secondary gray first, primary last, 12px apart. Never two filled buttons.
HTML
<div style="display:flex;gap:var(--cn-space-3);justify-content:flex-end"><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Cancel</span></button><button type="button" class="cn-button" data-variant="primary" data-size="md"><span class="cn-button__label">Confirm</span></button></div>

Matrix

variant ↓ / size →xssmmdlgxl
primary
secondary
outline
ghost
danger
link
secondary-color
link-color
danger-outline
danger-ghost
danger-link
Anatomy, props, states, rules

Anatomy

  • .cn-button <button> — The interactive element. Always a <button type="button|submit"> or an <a role="button"> when it navigates. Add data-icon-only when it holds a single icon and a visually hidden label.
  • .cn-button__icon <svg> · optional — Optional leading or trailing icon, 20px (16px on xs). Decorative: aria-hidden. Matches the label on filled buttons; secondary icons use semantic foreground tokens.
  • .cn-button__label <span> — The action label. Verb first ("Save changes"), sentence case, no trailing period.
  • .cn-button__spinner <span> · optional — Shown only while data-loading; replaces the icon slot, the label becomes invisible but keeps its width.

Props

propvalues (default outlined)meaning
variantprimary secondary outline ghost danger link secondary-color link-color danger-outline danger-ghost danger-linkprimary = the main filled action; outline = a single neutral edge; secondary = a quiet filled alternative; secondary-color = an action-colored outline; ghost = a transparent toolbar action. link and link-color align with text. The danger variants apply the same hierarchy to destructive actions.
sizexs sm md lg xlControl height and its matching padding, font and icon size.
icondefault onlyonly = square icon-only button (padding equals the vertical padding); requires aria-label.

States

stateselectormeaning
hover:hover:not(:disabled):not([aria-disabled="true"])Pointer over the element (mouse only; never the only cue). (native :hover)
active:active:not(:disabled):not([aria-disabled="true"])While pressed: the primary action deepens to bg-action-active. (native :active)
focus:focus-visibleKeyboard focus. The token-defined focus ring (red for danger*) is added under the existing ring and shadow; never on mouse click. (native :focus-visible)
disabled:disabled, &[aria-disabled="true"]Not interactive. Whole button at 50% opacity, cursor not-allowed, no hover. (disabled attribute (or aria-disabled="true"))
loading[data-loading]Async action in flight. Shows the spinner, hides the label visually, keeps width. (data-loading attribute)

Rules

  • Exactly one primary button per view region (a dialog, a page header, a form). If two actions feel primary, one of them is not.
  • Labels are verbs in sentence case: "Save changes", "Send invite", "Delete". Never "OK", "Submit", "Click here".
  • Icons are optional and decorative. Leading icon = what the action is (plus, upload); trailing icon = where it goes (arrow, external). Icon-only buttons set data-icon-only plus aria-label (or use IconButton).
  • Never change the height by adding padding. Height comes only from size: 32 / 36 / 40 / 44 / 48px, padding 10 / 12 / 14 / 16 / 18px.
  • Link variants (link, link-color, danger-link) have no padding and no height: they align with text. Use them for a low-emphasis action in a row of controls, never inside running text (that is Link).
  • Loading: set data-loading and aria-busy="true"; keep the label in the DOM so width does not jump. The fill becomes the hover fill.
  • Do not put two filled (primary + danger) buttons side by side. Danger actions live behind a confirm dialog.
  • Full-width buttons only on mobile sheets and auth forms.

Accessibility

  • Use <button>. Only use <a> when the action navigates, and then it is a Link or a button-styled anchor with a real href.
  • Icon-only buttons need an accessible name: aria-label on the button (or a cn-sr-only label inside), plus data-icon-only for the square padding.
  • disabled removes it from the tab order; if the user needs to know why, keep it enabled and show the reason on click.
  • Focus ring is shown on :focus-visible only, as a 4px box-shadow ring; never outline: none without replacement.
Actions

IconButton .cn-icon-buttonedit

A square (or round) button holding exactly one icon: icon-only button and its utility button. Same intents and sizes as Button, width equals height, the icon sits in a muted gray until hover, and the accessible name lives in aria-label.

Use: Use for a frequent, unambiguous action next to content: close, more actions, copy, delete a row, toggle a panel. If the icon needs a caption to be understood, use a Button with a label instead. Toggles (pin, bookmark, show calendar) use aria-pressed. Always pair with a Tooltip that repeats the aria-label.

Ghost (toolbar default)Transparent, gray-400 icon; gray-50 fill and a darker icon on hover.
HTML
<button type="button" class="cn-icon-button" data-variant="ghost" data-size="md" data-shape="square" aria-label="More actions"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.25"/><circle cx="8" cy="8" r="1.25"/><circle cx="12.5" cy="8" r="1.25"/></svg></button>
IntentsSame colors as Button. Ghost is the default because most icon buttons sit inside toolbars and rows.
HTML
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-3)"><button type="button" class="cn-icon-button" data-variant="primary" data-size="md" data-shape="square" aria-label="Add member"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg></button><button type="button" class="cn-icon-button" data-variant="secondary" data-size="md" data-shape="square" aria-label="Settings"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg></button><button type="button" class="cn-icon-button" data-variant="outline" data-size="md" data-shape="square" aria-label="Copy link"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg></button><button type="button" class="cn-icon-button" data-variant="ghost" data-size="md" data-shape="square" aria-label="Search"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg></button><button type="button" class="cn-icon-button" data-variant="danger" data-size="md" data-shape="square" aria-label="Delete row"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4.5h10M6.5 4.5V3h3v1.5M4.5 4.5l.6 8.5h5.8l.6-8.5"/></svg></button></div>
Utility (close and row actions)ButtonUtility: 28px (xs) and 32px (sm) boxes with a 6px radius, for file rows, tags and dialog headers.
HTML
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-3)"><button type="button" class="cn-icon-button" data-variant="utility" data-size="xs" data-shape="square" aria-label="Delete file"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4.5h10M6.5 4.5V3h3v1.5M4.5 4.5l.6 8.5h5.8l.6-8.5"/></svg></button><button type="button" class="cn-icon-button" data-variant="utility" data-size="sm" data-shape="square" aria-label="Close"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button><button type="button" class="cn-icon-button" data-variant="utility" data-size="md" data-shape="square" aria-label="Close panel"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
CircleRound for avatars and floating actions.
HTML
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-3)"><button type="button" class="cn-icon-button" data-variant="ghost" data-size="md" data-shape="circle" aria-label="Close"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button><button type="button" class="cn-icon-button" data-variant="outline" data-size="md" data-shape="circle" aria-label="Open profile"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg></button><button type="button" class="cn-icon-button" data-variant="primary" data-size="lg" data-shape="circle" aria-label="New message"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg></button></div>
Pressed (toggle)aria-pressed="true" keeps the gray-50 fill at rest so the on state is visible without hovering.
HTML
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-3)"><button type="button" class="cn-icon-button" data-variant="outline" data-size="md" data-shape="square" aria-label="Show calendar" aria-pressed="true"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg></button><button type="button" class="cn-icon-button" data-variant="outline" data-size="md" data-shape="square" aria-label="Show inbox" aria-pressed="false"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg></button><button type="button" class="cn-icon-button" data-variant="ghost" data-size="md" data-shape="square" aria-label="Pin to sidebar" aria-pressed="true"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 1.5l1.6 4.9 4.9 1.6-4.9 1.6L8 14.5l-1.6-4.9L1.5 8l4.9-1.6z"/></svg></button></div>
Sizes32 / 36 / 40 / 44 / 48px; the icon is 20px except on xs (16px).
HTML
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-3)"><button type="button" class="cn-icon-button" data-variant="outline" data-size="xs" data-shape="square" aria-label="Search"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg></button><button type="button" class="cn-icon-button" data-variant="outline" data-size="sm" data-shape="square" aria-label="Search"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg></button><button type="button" class="cn-icon-button" data-variant="outline" data-size="md" data-shape="square" aria-label="Search"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg></button><button type="button" class="cn-icon-button" data-variant="outline" data-size="lg" data-shape="square" aria-label="Search"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg></button><button type="button" class="cn-icon-button" data-variant="outline" data-size="xl" data-shape="square" aria-label="Search"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg></button></div>
Loading
HTML
<button type="button" class="cn-icon-button" data-variant="primary" data-size="md" data-shape="square" aria-label="Sending" data-loading aria-busy="true"><span class="cn-icon-button__spinner" aria-hidden="true"></span><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg></button>
Disabled
HTML
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-3)"><button type="button" class="cn-icon-button" data-variant="outline" data-size="md" data-shape="square" aria-label="Delete row" disabled><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4.5h10M6.5 4.5V3h3v1.5M4.5 4.5l.6 8.5h5.8l.6-8.5"/></svg></button><button type="button" class="cn-icon-button" data-variant="ghost" data-size="md" data-shape="square" aria-label="More actions" disabled><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.25"/><circle cx="8" cy="8" r="1.25"/><circle cx="12.5" cy="8" r="1.25"/></svg></button></div>

Recipes

Row actions in a card headerGhost, size sm, 4px apart. The "more" menu is always last.
HTML
<div style="display:flex;gap:var(--cn-space-1)"><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="Copy ID"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg></button><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="Open in new tab"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="More actions"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.25"/><circle cx="8" cy="8" r="1.25"/><circle cx="12.5" cy="8" r="1.25"/></svg></button></div>

Matrix

variant ↓ / size →xssmmdlgxl
primary
secondary
outline
ghost
danger
utility
Anatomy, props, states, rules

Anatomy

  • .cn-icon-button <button> — The interactive element. <button type="button"> with a mandatory aria-label; <a> only when it navigates.
  • .cn-icon-button__icon <svg> — The single icon, 20px (16px on xs). Decorative: aria-hidden="true". No text inside the button.
  • .cn-icon-button__spinner <span> · optional — Shown only while data-loading; the icon becomes invisible, the button keeps its size.

Props

propvalues (default outlined)meaning
variantprimary secondary outline ghost danger utilityghost = default for toolbars, table rows and card headers: transparent, gray-400 icon, gray-50 fill on hover (Tertiary icon-only). utility = the same colors in a smaller 6px-radius box for close (×) buttons, table-row and file-list actions (ButtonUtility tertiary: 28px on xs, 32px on sm). outline = white with a gray-300 ring when it stands alone on the canvas (Secondary icon-only). secondary = gray-50 fill with a ring next to a filled action. primary = the one filled action (rare: a "+" that is the main action on a small screen). danger = destructive, always behind a confirm.
sizexs sm md lg xlControl height and its matching padding, font and icon size.
shapesquare circlesquare = 8px radius (6px for utility), sits in toolbars and rows next to Buttons; circle = fully round, for avatars and floating actions. Do not mix shapes in one row.

States

stateselectormeaning
hover:hover:not(:disabled):not([aria-disabled="true"])Pointer over the element (mouse only; never the only cue). (native :hover)
active:active:not(:disabled):not([aria-disabled="true"])While pressed. has no distinct pressed style: it keeps the hover colors. (native :active)
focus:focus-visibleKeyboard focus. The 4px brand ring (red for danger) is added under the existing ring and shadow. (native :focus-visible)
disabled:disabled, &[aria-disabled="true"]Not interactive. Whole button at 50% opacity, cursor not-allowed, no hover. (disabled attribute (or aria-disabled="true"))
loading[data-loading]Async action in flight. Shows the spinner, hides the label visually, keeps width. (data-loading attribute)
pressed[aria-pressed="true"]Toggle is on. Gray-50 fill + default ink so the state is visible at rest, not only on hover. Use with ghost, utility or outline. (aria-pressed="true" (toggle buttons only))

Rules

  • aria-label is mandatory and names the action, not the icon: "Delete row", not "Trash".
  • Always pair with a Tooltip that shows the same text as aria-label; hover reveals what keyboard users already hear.
  • Ghost inside toolbars, table rows and card headers; utility for close buttons and file-list actions; outline when the button stands alone on the canvas; primary only when the icon action is the main action of the region.
  • No text inside an IconButton. The moment it needs a caption, it is a Button with a leading icon.
  • Toggles set aria-pressed and use ghost, utility or outline; the icon keeps its meaning in both states (a pin stays a pin, it does not become a cross).
  • Icon size comes from size; never scale the svg by hand or pad the button to change its footprint.
  • Touch targets: md (40px) or larger on mobile and in touch-first screens; xs/sm are for dense desktop rows only.
  • One shape per row. Square next to Buttons; circle for avatars and floating actions.
  • Destructive icon actions (danger, or a ghost trash icon) always confirm before acting.

Accessibility

  • Use <button type="button"> with aria-label (or aria-labelledby). An icon alone has no accessible name.
  • The svg is aria-hidden="true" and focusable="false"; the name comes from the button.
  • Toggle buttons expose aria-pressed="true|false" and keep the same aria-label in both states.
  • Focus ring on :focus-visible only, as a 4px box-shadow ring; never outline: none without a replacement.
  • While loading set aria-busy="true" and keep the aria-label; disabled removes it from the tab order.
Actions

ButtonGroup .cn-button-groupedit

A row (or column) of related Buttons. Attached groups fuse into one control by squaring the inner corners and overlapping the 1px rings (the reference button group); spaced groups just keep a consistent gap.

Use: Use attached for actions that belong to one object (Assign · Snooze · Archive), for split buttons (action + chevron menu) and for a toggle group where one option is on (aria-pressed on the selected child). Use spaced for a row of independent toolbar actions. For a rail-style single choice use SegmentedControl.

Attached (actions on one object)Outline buttons fuse into one control: inner corners squared, rings overlap by 1px, shadow-xs on the group.
HTML
<div class="cn-button-group" data-variant="attached" data-orientation="horizontal" role="group" aria-label="Conversation actions"><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Assign</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Snooze</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Archive</span></button></div>
Toggle group (one option on)The reference ButtonGroup: the selected child (aria-pressed="true") sits in the gray-50 fill with default ink.
HTML
<div class="cn-button-group" data-variant="attached" data-orientation="horizontal" role="group" aria-label="Range"><button type="button" class="cn-button" data-variant="outline" data-size="md" aria-pressed="true"><span class="cn-button__label">12 months</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md" aria-pressed="false"><span class="cn-button__label">30 days</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md" aria-pressed="false"><span class="cn-button__label">7 days</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md" aria-pressed="false"><span class="cn-button__label">24 hours</span></button></div>
With leading iconsIcons are gray-400 at rest and gray-500 on the selected item.
HTML
<div class="cn-button-group" data-variant="attached" data-orientation="horizontal" role="group" aria-label="View"><button type="button" class="cn-button" data-variant="outline" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M3 5h10M3 8h10M3 11h10"/></svg><span class="cn-button__label">Text</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md" aria-pressed="true"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg><span class="cn-button__label">Grid</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><span class="cn-button__label">Calendar</span></button></div>
Split buttonThe only case where an attached group is primary: the main action plus a chevron that opens a Menu.
HTML
<div class="cn-button-group" data-variant="attached" data-orientation="horizontal" role="group" aria-label="Send invite"><button type="button" class="cn-button" data-variant="primary" data-size="md"><span class="cn-button__label">Send invite</span></button><button type="button" class="cn-icon-button" data-variant="primary" data-size="md" data-shape="square" aria-label="More send options" aria-haspopup="menu" aria-expanded="false"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></button></div>
Attached icon buttons
HTML
<div class="cn-button-group" data-variant="attached" data-orientation="horizontal" role="group" aria-label="Row actions"><button type="button" class="cn-icon-button" data-variant="outline" data-size="md" data-shape="square" aria-label="Copy ID"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg></button><button type="button" class="cn-icon-button" data-variant="outline" data-size="md" data-shape="square" aria-label="Open in new tab"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button><button type="button" class="cn-icon-button" data-variant="outline" data-size="md" data-shape="square" aria-label="Delete"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4.5h10M6.5 4.5V3h3v1.5M4.5 4.5l.6 8.5h5.8l.6-8.5"/></svg></button></div>
Sizes36 / 40 / 44px, from the children's size.
HTML
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-4)"><div class="cn-button-group" data-variant="attached" data-orientation="horizontal" role="group" aria-label="Range small"><button type="button" class="cn-button" data-variant="outline" data-size="sm" aria-pressed="true"><span class="cn-button__label">Day</span></button><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Week</span></button><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Month</span></button></div><div class="cn-button-group" data-variant="attached" data-orientation="horizontal" role="group" aria-label="Range medium"><button type="button" class="cn-button" data-variant="outline" data-size="md" aria-pressed="true"><span class="cn-button__label">Day</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Week</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Month</span></button></div><div class="cn-button-group" data-variant="attached" data-orientation="horizontal" role="group" aria-label="Range large"><button type="button" class="cn-button" data-variant="outline" data-size="lg" aria-pressed="true"><span class="cn-button__label">Day</span></button><button type="button" class="cn-button" data-variant="outline" data-size="lg"><span class="cn-button__label">Week</span></button><button type="button" class="cn-button" data-variant="outline" data-size="lg"><span class="cn-button__label">Month</span></button></div></div>
SpacedIndependent tools keep their own radius; 12px apart.
HTML
<div class="cn-button-group" data-variant="spaced" data-orientation="horizontal" role="group" aria-label="List tools"><button type="button" class="cn-button" data-variant="outline" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M3 5h10M3 8h10M3 11h10"/></svg><span class="cn-button__label">Filter</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Sort</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg><span class="cn-button__label">Export</span></button></div>
Vertical attached with a disabled childChildren stretch to the widest label; text aligns to the start. A disabled child fades its content, not its ring.
HTML
<div class="cn-button-group" data-variant="attached" data-orientation="vertical" role="group" aria-label="Reorder"><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Move up</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Move down</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md" disabled><span class="cn-button__label">Remove</span></button></div>

Matrix

variant ↓ / orientation →horizontalvertical
attached
spaced
Anatomy, props, states, rules

Anatomy

  • .cn-button-group <div> — Inline-flex container with role="group" and an aria-label. Children are outline Buttons or IconButtons of the same size; attached groups carry the shadow-xs and the 8px outer radius.

Props

propvalues (default outlined)meaning
variantattached spacedattached = no gap, inner corners squared, rings overlap by 1px so the group reads as one control (the reference ButtonGroup); spaced = 12px gap, every button keeps its own radius and shadow.
orientationhorizontal verticalhorizontal = a row (toolbars, headers, split buttons); vertical = a stacked column (zoom controls, ordering, side rails). Vertical attached groups stretch children to the same width.

Rules

  • Group only actions that act on the same object or belong to the same tool. Cancel and Save are not a group; they are an action row.
  • All children share one size and the outline variant. The single exception is the split button: primary action + primary chevron.
  • Attached groups hold 2–5 children. Beyond that, use a toolbar of spaced groups or move the rest into a Menu.
  • Attached = one control (the corners say so). Spaced = several controls. Do not attach unrelated tools just to save space.
  • A toggle group marks exactly one child with aria-pressed="true" (a range, a view). For a rail with a lifted white item use SegmentedControl instead.
  • Vertical groups are rare: zoom controls, reorder handles, a rail of icon actions. Never a vertical group of text actions in a form.
  • Do not mix Button and IconButton sizes inside one group; the heights must match exactly for the rings to overlap cleanly.
  • Disabled children stay in the group (the layout must not jump); the reason shows in a Tooltip.

Accessibility

  • The root has role="group" and an aria-label naming what the actions act on ("Conversation actions").
  • Every child is a real <button>; IconButtons keep their aria-label. The group adds no keyboard behavior (Tab moves between children).
  • Toggle children expose aria-pressed="true|false"; a split button chevron exposes aria-haspopup="menu" and aria-expanded with its own aria-label.
  • Focus rings are raised above neighbours (z-index) so the ring is never clipped by the next button.
Actions

AppStoreButton .cn-app-store-buttonedit

The download badge for a mobile app: a 40px box with the store glyph on the left and two lines of text, a tiny kicker ("Download on the") over a bold store name. Dark by default, or outline on dark and busy backgrounds.

Use: Marketing pages, app footers and "get the app" banners, always as a pair (both stores) or a single badge when only one exists. Never inside the app shell, never as a generic download button (use Button with a leading icon).

Dark pairThe standard pair on a light page: inverse fill, thin grey border, 40px tall.
HTML
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-3)"><a href="#" class="cn-app-store-button" data-store="apple" data-theme="dark" aria-label="Download on the App Store"><svg class="cn-app-store-button__icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="13.5" r="6.5" fill="none" stroke="currentColor" stroke-width="1.75"/><path d="M12 7c0-2.2 1.3-3.5 3.5-3.5" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round"/></svg><span class="cn-app-store-button__kicker">Download on the</span><span class="cn-app-store-button__label">App Store</span></a><a href="#" class="cn-app-store-button" data-store="google" data-theme="dark" aria-label="Get it on Google Play"><svg class="cn-app-store-button__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M6.5 4.5v15l12.5-7.5z" fill="currentColor"/></svg><span class="cn-app-store-button__kicker">Get it on</span><span class="cn-app-store-button__label">Google Play</span></a></div>
Outline pairSurface fill with a hairline for footers and busy backgrounds.
HTML
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-3)"><a href="#" class="cn-app-store-button" data-store="apple" data-theme="outline" aria-label="Download on the App Store"><svg class="cn-app-store-button__icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="13.5" r="6.5" fill="none" stroke="currentColor" stroke-width="1.75"/><path d="M12 7c0-2.2 1.3-3.5 3.5-3.5" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round"/></svg><span class="cn-app-store-button__kicker">Download on the</span><span class="cn-app-store-button__label">App Store</span></a><a href="#" class="cn-app-store-button" data-store="google" data-theme="outline" aria-label="Get it on Google Play"><svg class="cn-app-store-button__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M6.5 4.5v15l12.5-7.5z" fill="currentColor"/></svg><span class="cn-app-store-button__kicker">Get it on</span><span class="cn-app-store-button__label">Google Play</span></a></div>
Outline on an inverse bandOn a dark band the outline theme keeps the badges readable; the dark theme would disappear.
HTML
<div style="background:var(--cn-color-bg-inverse);padding:var(--cn-space-5);border-radius:var(--cn-radius-lg)"><div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-3)"><a href="#" class="cn-app-store-button" data-store="apple" data-theme="outline" aria-label="Download on the App Store"><svg class="cn-app-store-button__icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="13.5" r="6.5" fill="none" stroke="currentColor" stroke-width="1.75"/><path d="M12 7c0-2.2 1.3-3.5 3.5-3.5" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round"/></svg><span class="cn-app-store-button__kicker">Download on the</span><span class="cn-app-store-button__label">App Store</span></a><a href="#" class="cn-app-store-button" data-store="google" data-theme="outline" aria-label="Get it on Google Play"><svg class="cn-app-store-button__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M6.5 4.5v15l12.5-7.5z" fill="currentColor"/></svg><span class="cn-app-store-button__kicker">Get it on</span><span class="cn-app-store-button__label">Google Play</span></a></div></div>
Single storeWhen the app exists on one store only. Do not fake the other badge.
HTML
<a href="#" class="cn-app-store-button" data-store="apple" data-theme="dark" aria-label="Download on the App Store"><svg class="cn-app-store-button__icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="13.5" r="6.5" fill="none" stroke="currentColor" stroke-width="1.75"/><path d="M12 7c0-2.2 1.3-3.5 3.5-3.5" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round"/></svg><span class="cn-app-store-button__kicker">Download on the</span><span class="cn-app-store-button__label">App Store</span></a>

Recipes

Get-the-app footer rowTitle and one line of copy on the left, the pair on the right; they wrap under the copy on narrow screens.
Lumen for iOS and Android
Reply to buyers and approve quotes from your phone.
HTML
<div style="display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--cn-space-4);max-width:640px;padding:var(--cn-space-5);border:1px solid var(--cn-color-border-default);border-radius:var(--cn-radius-card)"><div><div class="cn-text-heading-xs">Lumen for iOS and Android</div><div class="cn-text-body-sm" style="color:var(--cn-color-fg-muted)">Reply to buyers and approve quotes from your phone.</div></div><div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-3)"><a href="#" class="cn-app-store-button" data-store="apple" data-theme="dark" aria-label="Download on the App Store"><svg class="cn-app-store-button__icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="13.5" r="6.5" fill="none" stroke="currentColor" stroke-width="1.75"/><path d="M12 7c0-2.2 1.3-3.5 3.5-3.5" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round"/></svg><span class="cn-app-store-button__kicker">Download on the</span><span class="cn-app-store-button__label">App Store</span></a><a href="#" class="cn-app-store-button" data-store="google" data-theme="dark" aria-label="Get it on Google Play"><svg class="cn-app-store-button__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M6.5 4.5v15l12.5-7.5z" fill="currentColor"/></svg><span class="cn-app-store-button__kicker">Get it on</span><span class="cn-app-store-button__label">Google Play</span></a></div></div>

Matrix

Anatomy, props, states, rules

Anatomy

  • .cn-app-store-button <a> — The badge link (inline-grid: icon column + two text rows). href points to the store listing; aria-label repeats the two lines. Carries data-store and data-theme.
  • .cn-app-store-button__icon <svg> — 24px store glyph spanning both text rows. Monochrome, currentColor. aria-hidden.
  • .cn-app-store-button__kicker <span> — The small first line ("Download on the", "Get it on"), 11px medium, at 70% of the text color.
  • .cn-app-store-button__label <span> — The store name in 16px semibold with tight tracking.

Props

propvalues (default outlined)meaning
storeapple googleapple = "Download on the App Store"; google = "Get it on Google Play". The store also picks the glyph and the copy in the markup.
themedark outlinedark = inverse fill with light text and a thin grey border, the standard badge on light pages; outline = surface fill with a hairline and default ink, for dark or photographic backgrounds and for footers where a black block would be too heavy.

States

stateselectormeaning
hover:hover:not(:disabled):not([aria-disabled="true"])Pointer over the element (mouse only; never the only cue). (native :hover)
active:active:not(:disabled):not([aria-disabled="true"])While pressed. (native :active)
focus:focus-visibleKeyboard focus. Shows the 3px ring; never on mouse click. (native :focus-visible)

Rules

  • Always 40px tall, never scaled: the badge is a fixed-size mark, not a button that grows with the layout.
  • Show both stores side by side with a 12px gap, Apple first, in the same theme; show one badge only when the app really exists on one store.
  • Copy is fixed by the store: "Download on the / App Store" and "Get it on / Google Play". Do not translate the store names or invent kickers.
  • dark on light pages; outline on dark, photographic or tinted backgrounds and in footers. Never both themes on one page.
  • In production use the stores' official badge artwork inside the icon slot and follow their clear-space rules; the example glyphs are placeholders.
  • The badge links to the store listing (or a smart link) and opens in the same tab on mobile, a new tab on desktop.
  • Never place the badges inside the app shell or next to in-app actions; they belong to marketing surfaces and onboarding emails.

Accessibility

  • It is an <a href> with an aria-label that reads both lines ("Download on the App Store"); the glyph is aria-hidden.
  • The two text lines are real text, not an image, so they scale with the user's font size and remain translatable.
  • Focus ring on :focus-visible; on the dark theme it is the only hover-independent state, so keep it visible.
  • Kicker opacity is 70% of the text color, which keeps contrast ≥ 4.5:1 on both themes; do not lower it.
Actions

CloseButton .cn-close-buttonedit

The × that dismisses dialogs, drawers, alerts, toasts and chips. A square utility button with a grey icon and a subtle hover fill.

Use: Use only to dismiss the surface it sits in (top-right of dialogs/drawers/toasts, end of alerts/banners). It is never a "cancel" action in a footer; use a ghost Button there.

Sizes
HTML
<button type="button" class="cn-close-button" data-size="xs" data-theme="light" aria-label="Close"><svg class="cn-close-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button> <button type="button" class="cn-close-button" data-size="sm" data-theme="light" aria-label="Close"><svg class="cn-close-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button> <button type="button" class="cn-close-button" data-size="md" data-theme="light" aria-label="Close"><svg class="cn-close-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button> <button type="button" class="cn-close-button" data-size="lg" data-theme="light" aria-label="Close"><svg class="cn-close-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button>
Dark theme on an inverse surface
HTML
<div style="background:var(--cn-color-bg-inverse);padding:var(--cn-space-3);border-radius:var(--cn-radius-lg)"><button type="button" class="cn-close-button" data-size="sm" data-theme="dark" aria-label="Close"><svg class="cn-close-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>

Matrix

size ↓ / theme →lightdark
xs
sm
md
lg
Anatomy, props, states, rules

Anatomy

  • .cn-close-button <button> — Square hit area. Requires aria-label="Close".
  • .cn-close-button__icon <svg> — The × icon, 20px (sm) or 24px (md/lg).

Props

propvalues (default outlined)meaning
sizexs sm md lgxs 24px (chips, badges) · sm 36px (alerts, toasts, cards) · md 44px (dialogs, drawers) · lg 48px.
themelight darklight = grey icon on light surfaces; dark = translucent white icon for dark or brand surfaces.

States

stateselectormeaning
hover:hover:not(:disabled):not([aria-disabled="true"])Pointer over the element (mouse only; never the only cue). (native :hover)
active:active:not(:disabled):not([aria-disabled="true"])While pressed. (native :active)
focus:focus-visibleKeyboard focus. Shows the 3px ring; never on mouse click. (native :focus-visible)
disabled:disabled, &[aria-disabled="true"]Not interactive. Reduced opacity, no hover, cursor not-allowed. (disabled attribute (or aria-disabled="true"))

Rules

  • Always aria-label="Close" (or "Dismiss notification"), never a visible label.
  • Position: top-right, aligned with the title baseline, with the same inset as the surface padding minus 8px so the icon aligns with the content edge.
  • sm inside cards, alerts, toasts and chips; md/lg only in dialogs and drawers.
  • Escape must trigger the same close action wherever a close button exists.

Accessibility

  • It is a real <button type="button"> with an accessible name.
  • Focus returns to the trigger that opened the surface after closing.
Actions

SocialButton .cn-social-buttonedit

A sign-in button for an identity provider: the Button box with a 24px logo slot and a "Sign in with …" label. Three themes: the provider's brand fill, a white gray button with a monochrome logo, or a white button with the colored logo.

Use: Auth screens only (sign in, sign up, connect an account). Stack the providers you support in a fixed order under the email form, all in the same theme and size. Never use it for sharing links or as a generic icon button.

Gray (default)White with a hairline and a monochrome logo; the quiet default under an email form.
HTML
<div style="display:flex;flex-direction:column;gap:var(--cn-space-3);max-width:360px"><button type="button" class="cn-social-button" data-social="google" data-theme="gray" data-size="md"><svg class="cn-social-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 12h8.5a8.5 8.5 0 1 1-2.5-6"/></svg><span class="cn-social-button__label">Sign in with Google</span></button><button type="button" class="cn-social-button" data-social="apple" data-theme="gray" data-size="md"><svg class="cn-social-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="13.5" r="6.5"/><path d="M12 7c0-2.2 1.3-3.5 3.5-3.5"/></svg><span class="cn-social-button__label">Sign in with Apple</span></button><button type="button" class="cn-social-button" data-social="github" data-theme="gray" data-size="md"><svg class="cn-social-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M9.5 9.5L7 12l2.5 2.5M14.5 9.5L17 12l-2.5 2.5"/></svg><span class="cn-social-button__label">Sign in with GitHub</span></button><button type="button" class="cn-social-button" data-social="facebook" data-theme="gray" data-size="md"><svg class="cn-social-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M13.5 20v-6.5h2.2l.3-2.5h-2.5V9.6c0-.8.4-1.3 1.2-1.3h1.4V6.2h-2c-2 0-3.1 1.2-3.1 3.1V11H9v2.5h2V20"/></svg><span class="cn-social-button__label">Sign in with Facebook</span></button></div>
BrandThe provider fill with a white logo, for one prominent provider.
HTML
<div style="display:flex;flex-direction:column;gap:var(--cn-space-3);max-width:360px"><button type="button" class="cn-social-button" data-social="google" data-theme="brand" data-size="md"><svg class="cn-social-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 12h8.5a8.5 8.5 0 1 1-2.5-6"/></svg><span class="cn-social-button__label">Sign in with Google</span></button><button type="button" class="cn-social-button" data-social="apple" data-theme="brand" data-size="md"><svg class="cn-social-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="13.5" r="6.5"/><path d="M12 7c0-2.2 1.3-3.5 3.5-3.5"/></svg><span class="cn-social-button__label">Sign in with Apple</span></button><button type="button" class="cn-social-button" data-social="github" data-theme="brand" data-size="md"><svg class="cn-social-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M9.5 9.5L7 12l2.5 2.5M14.5 9.5L17 12l-2.5 2.5"/></svg><span class="cn-social-button__label">Sign in with GitHub</span></button><button type="button" class="cn-social-button" data-social="facebook" data-theme="brand" data-size="md"><svg class="cn-social-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M13.5 20v-6.5h2.2l.3-2.5h-2.5V9.6c0-.8.4-1.3 1.2-1.3h1.4V6.2h-2c-2 0-3.1 1.2-3.1 3.1V11H9v2.5h2V20"/></svg><span class="cn-social-button__label">Sign in with Facebook</span></button></div>
ColorWhite with the provider-colored logo, for a row of several providers.
HTML
<div style="display:flex;flex-direction:column;gap:var(--cn-space-3);max-width:360px"><button type="button" class="cn-social-button" data-social="google" data-theme="color" data-size="md"><svg class="cn-social-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 12h8.5a8.5 8.5 0 1 1-2.5-6"/></svg><span class="cn-social-button__label">Sign in with Google</span></button><button type="button" class="cn-social-button" data-social="apple" data-theme="color" data-size="md"><svg class="cn-social-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="13.5" r="6.5"/><path d="M12 7c0-2.2 1.3-3.5 3.5-3.5"/></svg><span class="cn-social-button__label">Sign in with Apple</span></button><button type="button" class="cn-social-button" data-social="github" data-theme="color" data-size="md"><svg class="cn-social-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M9.5 9.5L7 12l2.5 2.5M14.5 9.5L17 12l-2.5 2.5"/></svg><span class="cn-social-button__label">Sign in with GitHub</span></button><button type="button" class="cn-social-button" data-social="facebook" data-theme="color" data-size="md"><svg class="cn-social-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M13.5 20v-6.5h2.2l.3-2.5h-2.5V9.6c0-.8.4-1.3 1.2-1.3h1.4V6.2h-2c-2 0-3.1 1.2-3.1 3.1V11H9v2.5h2V20"/></svg><span class="cn-social-button__label">Sign in with Facebook</span></button></div>
Sizesmd / lg / xl follow the control heights; text grows to 16px on lg and xl.
HTML
<div style="display:flex;flex-direction:column;gap:var(--cn-space-3);max-width:360px"><button type="button" class="cn-social-button" data-social="google" data-theme="gray" data-size="md"><svg class="cn-social-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 12h8.5a8.5 8.5 0 1 1-2.5-6"/></svg><span class="cn-social-button__label">Continue with Google</span></button><button type="button" class="cn-social-button" data-social="google" data-theme="gray" data-size="lg"><svg class="cn-social-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 12h8.5a8.5 8.5 0 1 1-2.5-6"/></svg><span class="cn-social-button__label">Continue with Google</span></button><button type="button" class="cn-social-button" data-social="google" data-theme="gray" data-size="xl"><svg class="cn-social-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 12h8.5a8.5 8.5 0 1 1-2.5-6"/></svg><span class="cn-social-button__label">Continue with Google</span></button></div>
Full width and disableddata-full stretches the button to the form width; disabled keeps the layout and dims it.
HTML
<div style="display:flex;flex-direction:column;gap:var(--cn-space-3);max-width:360px"><button type="button" class="cn-social-button" data-social="github" data-theme="gray" data-size="md" data-full><svg class="cn-social-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M9.5 9.5L7 12l2.5 2.5M14.5 9.5L17 12l-2.5 2.5"/></svg><span class="cn-social-button__label">Sign in with GitHub</span></button><button type="button" class="cn-social-button" data-social="google" data-theme="gray" data-size="md" data-full disabled><svg class="cn-social-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 12h8.5a8.5 8.5 0 1 1-2.5-6"/></svg><span class="cn-social-button__label">Sign in with Google</span></button></div>

Recipes

Under an email formPrimary submit first, an "or" divider, then the providers in gray at the same size.
or
HTML
<div style="display:flex;flex-direction:column;gap:var(--cn-space-3);max-width:360px"><button type="submit" class="cn-button" data-variant="primary" data-size="md" style="width:100%"><span class="cn-button__label">Sign in</span></button><div style="display:flex;align-items:center;gap:var(--cn-space-3);color:var(--cn-color-fg-subtle)" class="cn-text-body-xs"><span style="flex:1;height:1px;background:var(--cn-color-border-default)"></span>or<span style="flex:1;height:1px;background:var(--cn-color-border-default)"></span></div><button type="button" class="cn-social-button" data-social="google" data-theme="gray" data-size="md" data-full><svg class="cn-social-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 12h8.5a8.5 8.5 0 1 1-2.5-6"/></svg><span class="cn-social-button__label">Sign in with Google</span></button><button type="button" class="cn-social-button" data-social="apple" data-theme="gray" data-size="md" data-full><svg class="cn-social-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="13.5" r="6.5"/><path d="M12 7c0-2.2 1.3-3.5 3.5-3.5"/></svg><span class="cn-social-button__label">Sign in with Apple</span></button></div>

Matrix

social ↓ / size →mdlgxl
google
apple
github
facebook
Anatomy, props, states, rules

Anatomy

  • .cn-social-button <button> — The button (or an <a> when the flow is a redirect). Full width in auth forms. Carries data-social, data-theme, data-size.
  • .cn-social-button__icon <svg> — 24px logo slot. Monochrome (currentColor) in brand and gray themes, the provider color in the color theme. aria-hidden.
  • .cn-social-button__label <span> — "Sign in with Google" / "Continue with Apple": verb + provider name, sentence case.

Props

propvalues (default outlined)meaning
socialgoogle apple github facebookThe identity provider. It selects the brand fill (brand theme) and the logo color (color theme); the logo itself comes from the markup.
themebrand gray colorgray = white, hairline, monochrome logo: the default that sits quietly under an email form; brand = the provider's fill with a white logo, for a single prominent provider; color = white with the provider-colored logo, for a row of several providers on marketing-style auth pages.
sizemd lg xlButton heights md / lg / xl (40 / 44 / 48px in the reference scale). Match the size of the email form's submit button.

States

stateselectormeaning
hover:hover:not(:disabled):not([aria-disabled="true"])Pointer over the element (mouse only; never the only cue). (native :hover)
active:active:not(:disabled):not([aria-disabled="true"])While pressed. (native :active)
focus:focus-visibleKeyboard focus. Shows the 3px ring; never on mouse click. (native :focus-visible)
disabled:disabled, &[aria-disabled="true"]Not interactive. Reduced opacity, no hover, cursor not-allowed. (disabled attribute (or aria-disabled="true"))

Rules

  • Label = verb + provider: "Sign in with Google" on sign-in screens, "Continue with Google" when the same button signs up. Never the provider name alone, never "Login".
  • One theme and one size per screen. gray under an email form; color when providers stand alone in a row; brand only for a single provider that is the main way in.
  • Order providers by usage in your audience and keep that order on every screen; put the email form above them, never below.
  • Full width (data-full) inside auth forms; intrinsic width only in a horizontal row of icon-heavy color buttons.
  • The logo slot is 24px; use the provider's official monochrome or color asset in production and respect its clear-space rules. The example glyphs are placeholders.
  • Never add a trailing arrow or a second icon; the logo is the only icon.
  • While the provider popup is open set aria-busy="true" and disable the other providers to avoid two flows at once.

Accessibility

  • It is a <button type="button"> (or an <a href> for redirect flows) with the visible label as its accessible name; the logo is aria-hidden.
  • Focus ring on :focus-visible only, stacked over the resting shadow so the ring is visible on filled brand buttons too.
  • Disabled providers stay visible and readable (opacity only); explain why when it matters ("Apple sign-in is not available in this region").
  • Contrast: brand fills use the 600 step with white text; gray and color themes use default ink on the surface.
Forms

SegmentedControl .cn-segmented-controledit

Single-choice group drawn as one rail: a gray-50 track with a hairline ring and 4px padding where the selected item lifts on a white surface with shadow-sm. Same heights (36 / 40px) and 14px semibold type as ButtonGroup, so the two sit side by side in a toolbar.

Use: Two to five mutually exclusive views or modes whose change is immediate (List / Board, Day / Week / Month, Monthly / Annual). For form data use Radio; for navigation between pages use Tabs; for actions on one object use ButtonGroup; for more than five options use Select.

Default40px rail: gray-50 with a hairline ring and 4px padding; the selected item is white with shadow-sm.
HTML
<div class="cn-segmented-control" role="radiogroup" data-size="md" data-variant="default" aria-label="View"><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="true" tabindex="0">List</button><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="false" tabindex="-1">Board</button><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="false" tabindex="-1">Timeline</button></div>
With icons20px gray-500 icons, 6px before the label; darker on the selected item.
HTML
<div class="cn-segmented-control" role="radiogroup" data-size="md" data-variant="default" aria-label="Inbox view"><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="true" tabindex="0"><svg class="cn-segmented-control__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg>Inbox</button><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="false" tabindex="-1"><svg class="cn-segmented-control__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg>Calendar</button><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="false" tabindex="-1"><svg class="cn-segmented-control__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg>People</button></div>
Icon-onlyEvery icon-only item carries an aria-label.
HTML
<div class="cn-segmented-control" role="radiogroup" data-size="md" data-variant="default" aria-label="Layout"><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="true" aria-label="List layout" tabindex="0"><svg class="cn-segmented-control__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M3 5h10M3 8h10M3 11h10"/></svg></button><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="false" aria-label="Grid layout" tabindex="-1"><svg class="cn-segmented-control__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg></button><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="false" aria-label="Settings layout" tabindex="-1"><svg class="cn-segmented-control__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg></button></div>
Full width
HTML
<div class="cn-segmented-control" role="radiogroup" data-size="md" data-variant="full" aria-label="Billing cycle" style="max-width:360px"><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="false" tabindex="-1">Monthly</button><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="true" tabindex="0">Annual · save 20%</button></div>
Small36px rail with 28px items and radius 10.
HTML
<div class="cn-segmented-control" role="radiogroup" data-size="sm" data-variant="default" aria-label="Range"><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="false" tabindex="-1">Day</button><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="true" tabindex="0">Week</button><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="false" tabindex="-1">Month</button><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="false" tabindex="-1">Quarter</button></div>
Disabled itemPDF export is a Scale feature: the item stays visible at 50%.
HTML
<div class="cn-segmented-control" role="radiogroup" data-size="md" data-variant="default" aria-label="Export format"><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="true" tabindex="0">CSV</button><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="false" tabindex="-1">Excel</button><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="false" tabindex="-1" disabled aria-disabled="true">PDF</button></div>
Disabled group
HTML
<div class="cn-segmented-control" role="radiogroup" data-size="md" data-variant="default" aria-label="View" data-disabled aria-disabled="true"><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="true" tabindex="-1" disabled>List</button><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="false" tabindex="-1" disabled>Board</button></div>

Recipes

Next to a ButtonGroup in a toolbarBoth are 40px tall with 14px semibold labels; the rail is for a view, the group for a range.
HTML
<div style="display:flex;align-items:center;gap:var(--cn-space-3)"><div class="cn-segmented-control" role="radiogroup" data-size="md" data-variant="default" aria-label="View"><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="true" tabindex="0">List</button><button type="button" role="radio" class="cn-segmented-control__item" aria-checked="false" tabindex="-1">Board</button></div><div class="cn-button-group" data-variant="attached" data-orientation="horizontal" role="group" aria-label="Range"><button type="button" class="cn-button" data-variant="outline" data-size="md" aria-pressed="true"><span class="cn-button__label">30 days</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md" aria-pressed="false"><span class="cn-button__label">7 days</span></button></div></div>

Matrix

variant ↓ / size →smmd
default
full
Anatomy, props, states, rules

Anatomy

  • .cn-segmented-control <div> — The rail: inline-flex, gray-50 fill, 1px gray-200 inset ring, 4px padding, radius 10 (sm) / 12 (md), 4px between items. role="radiogroup" with an aria-label.
  • .cn-segmented-control__item <button> — One option: <button role="radio" aria-checked>. 28px (sm) / 32px (md) tall, padding 10px, radius 6 / 8, 14px semibold gray-600 until selected.
  • .cn-segmented-control__icon <svg> · optional — Optional leading icon (16px on sm, 20px on md) inside an item, gray-500, or the only content with aria-label on the item. Decorative.

Props

propvalues (default outlined)meaning
sizesm mdmd = 40px rail with 32px items (matches md controls and a md ButtonGroup); sm = 36px rail with 28px items for cards, tables and popovers.
variantdefault fulldefault = items at their content width, the rail hugs them; full = the rail fills its container and every item takes an equal share (mobile sheets, narrow cards).

States

stateselectormeaning
selected[aria-checked="true"], &[data-selected]On the __item (not the root): white surface, default ink, shadow-sm; the icon darkens. Exactly one per group. (aria-checked="true" on the item button (data-selected also accepted))
hover:hover:not(:disabled)On an unselected __item: text turns to the default ink and the icon darkens; no background change. (native :hover on the item)
focus:focus-visibleOn the __item: the 4px brand ring on the item itself, raised above its neighbours. (native :focus-visible on the item)
item-disabled:disabledOn the __item: 50% opacity, cursor not-allowed; stays visible so the option is known to exist. (disabled on the item button)
disabled[data-disabled]Whole group at 50% opacity and not interactive. (data-disabled on the root)

Rules

  • Use for 2–5 mutually exclusive views or modes whose change is immediate. For form data use Radio; for page navigation use Tabs; for actions on one object use ButtonGroup.
  • Labels are one or two words in sentence case, or an icon with aria-label. Keep every item in the same voice: all nouns or all icons, never mixed.
  • One item is always selected; there is no empty state and no toggling off.
  • variant="full" only inside narrow containers (mobile sheets, cards ≤ 400px) so each item stretches equally; default keeps items at content width.
  • md (40px) matches md controls in a toolbar; sm (36px) sits inside tables, cards and popovers. Never place a SegmentedControl taller than the controls beside it.
  • Maximum five items. Beyond that the choice becomes a Select or Tabs.
  • Do not mix action buttons into the rail; put related actions in a ButtonGroup beside it, 12px away.

Accessibility

  • The root has role="radiogroup" with aria-label (or aria-labelledby); each item is a <button role="radio" aria-checked="true|false">.
  • Roving tabindex: only the selected item has tabindex="0"; Left/Right (and Up/Down) move selection and check the new item, Home/End jump.
  • Icon-only items require aria-label; the svg is aria-hidden.
  • Disabled item: disabled plus aria-disabled="true"; a disabled group sets data-disabled and aria-disabled on the root.
  • Selection change must not move focus or navigate. If it changes the URL, use Tabs instead.
Forms

Field .cn-fieldedit

Label, control and one line of hint or error text stacked 6px apart (the reference text-field rhythm). The label is 14px medium with an optional brand asterisk and help icon; the hint is 14px muted and turns red when the field is invalid. It owns the words around a control so every form reads the same way.

Use: Wrap every Input, Textarea, Select, Combobox, Slider and FileDropzone in a Field so it gets a visible label and a place for hint and error text. Do not wrap Checkbox, Radio or Switch (they carry their own label; group them with a radio-group or a fieldset). Use layout="horizontal" only on settings pages where many short fields stack under one another.

Vertical (default)Label 14px medium, 6px, the control, 6px, a 14px muted hint.

The legal name that appears on invoices.

HTML
<div class="cn-field" data-layout="vertical" data-size="md"  style="max-width:360px"><label class="cn-field__label" for="field-company">Company name</label><div class="cn-field__control"><div class="cn-input" data-variant="default" data-size="md"><input class="cn-input__field" id="field-company" type="text" placeholder="Lumen Studio" aria-describedby="field-company-hint"></div></div><p class="cn-field__helper" id="field-company-hint">The legal name that appears on invoices.</p></div>
Required with help iconThe brand asterisk sits 2px after the label; the 16px help icon opens a Tooltip.

We only send billing receipts here.

HTML
<div class="cn-field" data-layout="vertical" data-size="md"  style="max-width:360px"><label class="cn-field__label" for="field-email">Work email<span class="cn-field__required" aria-hidden="true">*</span><button type="button" class="cn-field__tooltip" aria-label="Used to sign in and to receive receipts"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg></button></label><div class="cn-field__control"><div class="cn-input" data-variant="default" data-size="md"><input class="cn-input__field" id="field-email" type="email" placeholder="maya@lumen.app" required aria-describedby="field-email-hint"></div></div><p class="cn-field__helper" id="field-email-hint">We only send billing receipts here.</p></div>
Optional markerFor forms that mark optional fields instead of required ones.
https://
HTML
<div class="cn-field" data-layout="vertical" data-size="md"  style="max-width:360px"><label class="cn-field__label" for="field-site">Website <span class="cn-field__optional">(optional)</span></label><div class="cn-field__control"><div class="cn-input" data-variant="default" data-size="md"><span class="cn-input__affix">https://</span><input class="cn-input__field" id="field-site" type="url" placeholder="lumen.app"></div></div></div>
InvalidThe error replaces the hint in the danger color and the asterisk turns red; the text says how to fix it.

We only send billing receipts here.

Enter a complete address, like maya@lumen.app.

HTML
<div class="cn-field" data-layout="vertical" data-size="md" data-invalid style="max-width:360px"><label class="cn-field__label" for="field-email2">Work email<span class="cn-field__required" aria-hidden="true">*</span></label><div class="cn-field__control"><div class="cn-input" data-variant="default" data-size="md"><input class="cn-input__field" id="field-email2" type="email" value="maya@lumen" aria-invalid="true" required aria-describedby="field-email2-error"></div></div><p class="cn-field__helper" id="field-email2-hint">We only send billing receipts here.</p><p class="cn-field__error" id="field-email2-error">Enter a complete address, like maya@lumen.app.</p></div>
Horizontal (settings page)A 192px label column; the label aligns with the 40px control.

Shown to teammates in comments and activity.

HTML
<div class="cn-field" data-layout="horizontal" data-size="md"  style="max-width:640px"><label class="cn-field__label" for="field-display">Display name</label><div class="cn-field__control"><div class="cn-input" data-variant="default" data-size="md"><input class="cn-input__field" id="field-display" type="text" value="Maya Chen"></div></div><p class="cn-field__helper" id="field-display-hint">Shown to teammates in comments and activity.</p></div>
DisabledThe control fades; the words stay readable.

Managed by Aisha Khan, your billing admin.

HTML
<div class="cn-field" data-layout="vertical" data-size="md" data-disabled style="max-width:360px"><label class="cn-field__label" for="field-plan">Plan</label><div class="cn-field__control"><div class="cn-input" data-variant="default" data-size="md"><input class="cn-input__field" id="field-plan" type="text" value="Team (annual)" disabled></div></div><p class="cn-field__helper" id="field-plan-hint">Managed by Aisha Khan, your billing admin.</p></div>
SmallA 14px label with a 12px hint, for sm controls.
USD

Orders below this value are hidden.

HTML
<div class="cn-field" data-layout="vertical" data-size="sm"  style="max-width:280px"><label class="cn-field__label" for="field-min">Minimum order value</label><div class="cn-field__control"><div class="cn-input" data-variant="default" data-size="sm"><span class="cn-input__affix">USD</span><input class="cn-input__field" id="field-min" type="number" value="5000"></div></div><p class="cn-field__helper" id="field-min-hint">Orders below this value are hidden.</p></div>

Recipes

Settings sectionHorizontal fields inside a Card, stacked 20px apart. One primary action in the footer.

Workspace

Name and region are visible to every member.

app.lumen.app/

Lowercase letters, numbers and dashes.

Contact support to migrate regions.

HTML
<section class="cn-card" data-variant="default" data-padding="md" style="max-width:640px;width:100%"><header class="cn-card__header"><div><h3 class="cn-card__title">Workspace</h3><p class="cn-card__description">Name and region are visible to every member.</p></div></header><div class="cn-card__body" style="display:flex;flex-direction:column;gap:var(--cn-space-5)"><div class="cn-field" data-layout="horizontal" data-size="md"  style=""><label class="cn-field__label" for="ws-name">Workspace name</label><div class="cn-field__control"><div class="cn-input" data-variant="default" data-size="md"><input class="cn-input__field" id="ws-name" type="text" value="Lumen Studio"></div></div></div><div class="cn-field" data-layout="horizontal" data-size="md"  style=""><label class="cn-field__label" for="ws-slug">URL</label><div class="cn-field__control"><div class="cn-input" data-variant="default" data-size="md"><span class="cn-input__affix">app.lumen.app/</span><input class="cn-input__field" id="ws-slug" type="text" value="lumen-studio"></div></div><p class="cn-field__helper" id="ws-slug-hint">Lowercase letters, numbers and dashes.</p></div><div class="cn-field" data-layout="horizontal" data-size="md" data-disabled style=""><label class="cn-field__label" for="ws-region">Data region</label><div class="cn-field__control"><div class="cn-input" data-variant="default" data-size="md"><input class="cn-input__field" id="ws-region" type="text" value="Europe (Frankfurt)" disabled></div></div><p class="cn-field__helper" id="ws-region-hint">Contact support to migrate regions.</p></div></div><footer class="cn-card__footer" style="justify-content:flex-end"><button type="button" class="cn-button" data-variant="primary" data-size="sm"><span class="cn-button__label">Save changes</span></button></footer></section>

Matrix

layout ↓ / size →smmd
vertical

The legal name that appears on invoices.

The legal name that appears on invoices.

horizontal

The legal name that appears on invoices.

The legal name that appears on invoices.

Anatomy, props, states, rules

Anatomy

  • .cn-field <div> — The wrapper: a 6px vertical stack by default; a label-column grid in horizontal layout. Carries data-invalid and data-disabled.
  • .cn-field__label <label> — Visible label with for="<control id>": 14px medium in the default ink, a flex row with 2px gaps for the asterisk and help icon. Sentence case, no trailing colon, 1–4 words ("Company name").
  • .cn-field__required <span> · optional — Optional brand asterisk right after the label text (2px away) marking a required field; turns red when invalid. aria-hidden — the control carries required.
  • .cn-field__tooltip <button> · optional — Optional 16px help icon after the label that opens a Tooltip on hover/focus (aria-label = the tooltip text). Gray-500, darker on hover.
  • .cn-field__optional <span> · optional — Muted "(optional)" marker inside the label, regular weight, for forms that mark optional fields instead of required ones.
  • .cn-field__control <div> — Slot holding exactly one control (an Input, Select, Textarea, Combobox…). No styling besides min-width: 0.
  • .cn-field__helper <p> · optional — The hint: one short line under the control, 14px (12px on sm) muted: format, example or limit ("Shown on invoices"). Hidden while the field is invalid.
  • .cn-field__error <p> · optional — Validation message in the same size as the hint, in the danger color (an optional 16px icon may lead it). Hidden until data-invalid is on the root; give it an id and reference it from the control with aria-describedby.

Props

propvalues (default outlined)meaning
layoutvertical horizontalvertical = label above the control (forms, dialogs, onboarding); horizontal = label in a fixed 192px left column with the control on the right (settings pages with many short fields).
sizesm mdmd = 14px label and 14px hint, pairs with md/lg controls; sm = 14px label and 12px hint, pairs with sm controls in dense panels and filters (the reference size="sm" hint).

States

stateselectormeaning
invalid[data-invalid]Shows the error in the danger color, hides the hint and turns the asterisk red. Set together with aria-invalid="true" on the control. (data-invalid on the root)
disabled[data-disabled]The control fades on its own (50% opacity); the label and hint stay readable and the label shows the not-allowed cursor. Set together with disabled on the control. (data-disabled on the root)

Rules

  • Every control gets a visible label. Placeholder text is an example value, never the label.
  • Labels are 1–4 words in sentence case with no colon. Name what goes in the field ("Work email"), not the action ("Enter your email").
  • Pick one convention per form: mark required fields with the brand asterisk, or mark optional ones with "(optional)". Never both.
  • Hint text is one sentence in present tense that answers "what format" or "why do you need it". If it needs two lines, the label or the flow is wrong.
  • Invalid: set data-invalid on the Field AND aria-invalid="true" on the control. The error replaces the hint and says how to fix it ("Enter a complete address"), not just "Invalid".
  • The help icon carries context that does not fit the hint (why, where it is used); it never replaces the hint or the error.
  • Use one layout per form. Horizontal settings fields have a 192px label column and stack vertically below 640px of available width.
  • Fields stack 20px apart; two related short fields (city + postal code) may share a row 16px apart.
  • Constrain short values (postal code, quantity, currency) with max-width on the Field, not on the control.

Accessibility

  • The <label for> references the control id, so clicking the label focuses the control and screen readers announce it.
  • The asterisk is aria-hidden; put required on the control (or aria-required) so the requirement is announced.
  • The help icon is a real <button> with aria-label equal to the tooltip text; the tooltip is referenced with aria-describedby.
  • When invalid, the control has aria-invalid="true" and aria-describedby pointing at the error id; when valid, aria-describedby points at the hint id.
  • The error text is static in the flow. Announce submit results with a separate live region, not by making every error aria-live.
Forms

Input .cn-inputedit

Single-line text field: a white wrapper with a 1px gray-300 inset ring and shadow-xs, radius 8, 36 / 40 / 44px tall. Optional leading icon, inline prefix or suffix, a trailing help icon (tooltip) and a keyboard-shortcut chip. The wrapper carries the ring; the native input is bare.

Use: Free text, numbers, email, search, URLs. Pair with Field for label + hint + error. For choices use Select or Combobox; for multi-line text use Textarea.

Default40px tall, 12px padding, gray-300 ring and shadow-xs, radius 8.
HTML
<div class="cn-input" data-variant="default" data-size="md" style="max-width:320px"><input class="cn-input__field" type="email" placeholder="maya@lumen.app" aria-label="Work email"></div>
Sizes36 / 40 / 44px. Text is 14px on sm and md, 16px on lg; padding grows to 14px on lg.
HTML
<div style="display:flex;flex-direction:column;gap:var(--cn-space-4)"><div class="cn-input" data-variant="default" data-size="sm" style="max-width:320px"><input class="cn-input__field" type="text" placeholder="Small · 36px" aria-label="Small"></div><div class="cn-input" data-variant="default" data-size="md" style="max-width:320px"><input class="cn-input__field" type="text" placeholder="Medium · 40px" aria-label="Medium"></div><div class="cn-input" data-variant="default" data-size="lg" style="max-width:320px"><input class="cn-input__field" type="text" placeholder="Large · 44px" aria-label="Large"></div></div>
With leading iconIcon 20px (16px on sm), gray-500, inset by the padding; the text starts 8px after it.
HTML
<div style="display:flex;flex-direction:column;gap:var(--cn-space-4)"><div class="cn-input" data-variant="default" data-size="md" style="max-width:320px"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search workspaces…" aria-label="Search"></div><div class="cn-input" data-variant="default" data-size="sm" style="max-width:320px"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Filter members…" aria-label="Filter members"></div></div>
With prefix and suffixInline affixes in the muted color, no fill and no rule; the text follows 8px after a prefix.
https://
USD/ month
HTML
<div style="display:flex;flex-direction:column;gap:var(--cn-space-4)"><div class="cn-input" data-variant="default" data-size="md" style="max-width:320px"><span class="cn-input__affix">https://</span><input class="cn-input__field" type="url" placeholder="lumen.app" aria-label="Website"></div><div class="cn-input" data-variant="default" data-size="md" style="max-width:320px"><span class="cn-input__affix">USD</span><input class="cn-input__field" type="number" value="2500" aria-label="Monthly budget"><span class="cn-input__affix">/ month</span></div></div>
With help icon and shortcutThe help icon (16px, gray-500) sits 12px from the edge and opens a Tooltip; the ⌘K chip sits 8px from the edge and hides under 768px.
HTML
<div style="display:flex;flex-direction:column;gap:var(--cn-space-4)"><div class="cn-input" data-variant="default" data-size="md" style="max-width:320px"><input class="cn-input__field" type="text" placeholder="Workspace name" aria-label="Workspace name" aria-describedby="in-help"><button type="button" class="cn-input__tooltip" aria-label="More information"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg></button></div><div class="cn-input" data-variant="default" data-size="md" style="max-width:320px"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search…" aria-label="Search"><span class="cn-input__shortcut" aria-hidden="true">⌘K</span></div></div>
Filled (toolbar)Gray-50 fill without a ring; the ring appears on focus.
HTML
<div class="cn-input" data-variant="filled" data-size="sm" style="max-width:240px"><input class="cn-input__field" type="text" placeholder="Filter…" aria-label="Filter"></div>
InvalidRed-300 ring; the help slot turns into the danger alert icon. Pair with a Field error such as "Enter a complete address".
HTML
<div class="cn-input" data-variant="default" data-size="md" style="max-width:320px"><input class="cn-input__field" type="email" value="maya@lumen" aria-invalid="true" aria-label="Work email" aria-describedby="in-err"><button type="button" class="cn-input__tooltip" aria-label="More information"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg></button></div>
DisabledSame fill and ring at 50% opacity, cursor not-allowed.
HTML
<div class="cn-input" data-variant="default" data-size="md" style="max-width:320px"><input class="cn-input__field" type="text" value="Lumen Studio" disabled aria-label="Workspace"></div>

Recipes

Search with shortcut in a topbarA search field with a leading icon and the ⌘K chip next to an outline button of the same height.
HTML
<div style="display:flex;align-items:center;gap:var(--cn-space-3);max-width:560px"><div class="cn-input" data-variant="default" data-size="md" style="flex:1 1 auto"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search projects, people, files…" aria-label="Search"><span class="cn-input__shortcut" aria-hidden="true">⌘K</span></div><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Filters</span></button></div>

Matrix

variant ↓ / size →smmdlg
default
filled
Anatomy, props, states, rules

Anatomy

  • .cn-input <div> — Wrapper that draws the ring, background, radius, shadow-xs and the focus ring (:focus-within). Flex row, height from size.
  • .cn-input__icon <svg> · optional — Optional leading icon (search, mail): 16px on sm, 20px on md and lg, gray-500, inset by the horizontal padding; the text starts 8px after it (36 / 40 / 42px from the edge). Decorative.
  • .cn-input__affix <span> · optional — Optional inline prefix or suffix text ("https://", "USD", "kg") in the muted color, no fill and no rule: padded 12px on the outside (14px on lg) and 6–8px next to the text.
  • .cn-input__field <input> — The native <input>. No border, no outline, fills the wrapper; padding 12px (14px on lg) on each side.
  • .cn-input__tooltip <button> · optional — Optional trailing 16px help icon that opens a Tooltip on hover/focus (aria-label = the tooltip text). Gray-500, darker on hover. When the field is invalid the same slot shows the alert icon in the danger color.
  • .cn-input__shortcut <span> · optional — Optional keyboard-shortcut chip at the trailing edge ("⌘K"): 12px medium gray-500 on white, 1px gray-200 ring, radius 4, padding 1px 4px, 6 / 8 / 10px from the edge. aria-hidden; hidden under 768px.

Props

propvalues (default outlined)meaning
sizesm md lgControl height and its matching padding, font and icon size.
variantdefault filleddefault = white with the gray-300 ring and shadow-xs (the reference input; forms, dialogs, settings); filled = gray-50 fill without ring or shadow until focus, for dense toolbars and table filters (no reference equivalent).

States

stateselectormeaning
hover:hover:not([data-disabled])Pointer over the field. The reference has no hover change; presets with a distinct border-control-hover strengthen the ring. (native)
focus:focus-withinThe inset ring turns to the brand color and the 4px brand focus ring appears around the wrapper (never on the input itself). (native :focus-within on the wrapper)
invalid[data-invalid], &:has([aria-invalid="true"])Red-300 ring; on focus the ring stays red and the focus ring turns red. The help slot shows the alert icon in the danger color. Always accompanied by a Field error. (aria-invalid="true" on the input, or data-invalid on the wrapper)
disabled[data-disabled], &:has(:disabled)Whole control at 50% opacity, cursor not-allowed, same fill and ring. (disabled on the input)
readonly:has([readonly])Looks like default; text stays selectable and the focus ring still shows. (readonly on the input)

Rules

  • Always inside a Field with a visible label, except search inputs with a leading icon and a placeholder that says what is searched.
  • Placeholder is an example, not a label ("maya@lumen.app", not "Email").
  • Width comes from the layout (100% of the Field). Never set a fixed pixel width; use max-width on the Field for short values.
  • Sizes: md (40px) in forms and dialogs; sm (36px) inside toolbars, tables and filters; lg (44px) on marketing and auth screens where 16px text is wanted.
  • Never change the height by adding padding. Height comes only from size: 36 / 40 / 44px with 12 / 12 / 14px horizontal padding.
  • The help icon opens a Tooltip and never replaces the hint; the shortcut chip is decorative and only appears on fields that really have a global shortcut.
  • Error state must show a Field error under the input; the red ring and icon alone are not enough.
  • On mobile the font-size must be ≥ 16px to prevent Safari zoom: use size lg or the pattern in DESIGN.md.

Accessibility

  • Every input has an accessible name: a <label for> from the Field, or aria-label for standalone search.
  • aria-invalid="true" and aria-describedby pointing to the Field error id when invalid.
  • The help icon is a real <button> with aria-label equal to the tooltip text; the shortcut chip is aria-hidden (announce the shortcut in the tooltip or hint).
  • Keep native type attributes (email, url, number, search) for keyboards and validation.
  • The focus ring shows on the wrapper through :focus-within; never remove it on the input without the wrapper replacement.
Forms

Textarea .cn-textareaedit

Multi-line text field with the same ring, radius 8, shadow-xs and focus ring as Input. Padding 12px (sm) or 12×14px (md, lg); the height comes from rows. The wrapper carries the ring; the native textarea is bare.

Use: Free-form text longer than one line: notes, messages, descriptions, addresses. For one line use Input; for rich formatting use an editor component. Pair with Field for label, hint and a character count.

DefaultThree rows, 12×14px padding, gray-300 ring and shadow-xs, radius 8.
HTML
<div class="cn-textarea" data-size="md" data-resize="vertical" style="max-width:480px"><textarea class="cn-textarea__field" rows="3" placeholder="Describe the change for the release notes…" aria-label="Description"></textarea></div>
With contentThe wrapper grows with rows; drag the corner to make it taller.
HTML
<div class="cn-textarea" data-size="md" data-resize="vertical" style="max-width:480px"><textarea class="cn-textarea__field" rows="3" aria-label="Notes">Daniel Costa asked for the onboarding checklist to be split by role. Maya Chen will draft the designer track before the Friday review.</textarea></div>
Sizessm 12px all round; md and lg 12px vertical, 14px horizontal. Text is 16px on lg.
HTML
<div style="display:flex;flex-direction:column;gap:var(--cn-space-4)"><div class="cn-textarea" data-size="sm" data-resize="vertical" style="max-width:480px"><textarea class="cn-textarea__field" rows="3" placeholder="Small · 12px padding, 14px text" aria-label="Small"></textarea></div><div class="cn-textarea" data-size="md" data-resize="vertical" style="max-width:480px"><textarea class="cn-textarea__field" rows="3" placeholder="Medium · 12×14px padding, 14px text" aria-label="Medium"></textarea></div><div class="cn-textarea" data-size="lg" data-resize="vertical" style="max-width:480px"><textarea class="cn-textarea__field" rows="3" placeholder="Large · 12×14px padding, 16px text" aria-label="Large"></textarea></div></div>
Fixed heightresize="none" hides the handle for strict layouts.
HTML
<div class="cn-textarea" data-size="sm" data-resize="none" style="max-width:360px"><textarea class="cn-textarea__field" rows="3" placeholder="Internal note (not visible to the client)" aria-label="Internal note"></textarea></div>
InvalidRed-300 ring. Pair with a Field error such as "Write at least 40 characters".
HTML
<div class="cn-textarea" data-size="md" data-resize="vertical" style="max-width:480px"><textarea class="cn-textarea__field" rows="3" aria-invalid="true" aria-label="Description" aria-describedby="ta-err">ok</textarea></div>
DisabledSame fill and ring at 50% opacity; no resize handle.
HTML
<div class="cn-textarea" data-size="md" data-resize="vertical" style="max-width:480px"><textarea class="cn-textarea__field" rows="3" disabled aria-label="Terms">Standard terms apply to this workspace and cannot be edited after the plan is signed.</textarea></div>

Recipes

With character countThe count lives in the Field hint, right-aligned, tabular numerals. Turn it to fg-danger only when the limit is exceeded.

Shown on your public profile.109 / 240

HTML
<div class="cn-field" data-layout="vertical" data-size="md" style="max-width:480px"><label class="cn-field__label" for="ta-bio">Public description</label><div class="cn-field__control"><div class="cn-textarea" data-size="md" data-resize="vertical"><textarea class="cn-textarea__field" id="ta-bio" rows="3" maxlength="240" aria-describedby="ta-bio-helper">Lumen helps product teams turn research into decisions: interviews, notes and evidence in one workspace.</textarea></div></div><p class="cn-field__helper" id="ta-bio-helper" style="display:flex;justify-content:space-between;gap:var(--cn-space-3)"><span>Shown on your public profile.</span><span class="cn-tabular">109 / 240</span></p></div>

Matrix

size ↓ / resize →nonevertical
sm
md
lg
Anatomy, props, states, rules

Anatomy

  • .cn-textarea <div> — Wrapper that draws the ring, background, radius, shadow-xs and the focus ring (:focus-within).
  • .cn-textarea__field <textarea> — The native <textarea>. No border, no outline, fills the wrapper. Set rows to size it (3 in forms); the wrapper grows with it and with a vertical resize.

Props

propvalues (default outlined)meaning
sizesm md lgControl height and its matching padding, font and icon size.
resizenone verticalvertical = the user can drag the corner to make it taller (default for notes and messages); none = fixed height, for inputs with a strict length or inside dense layouts.

States

stateselectormeaning
hover:hover:not([data-disabled])Pointer over the field. The reference has no hover change; presets with a distinct border-control-hover strengthen the ring. (native)
focus:focus-withinThe inset ring turns to the brand color and the 4px brand focus ring appears around the wrapper. (native :focus-within on the wrapper)
invalid[data-invalid], &:has([aria-invalid="true"])Red-300 ring; on focus the focus ring turns red. Always accompanied by a Field error. (aria-invalid="true" on the textarea, or data-invalid on the wrapper)
disabled[data-disabled], &:has(:disabled)Whole control at 50% opacity, cursor not-allowed, no resize handle. (disabled on the textarea)

Rules

  • Start at 3 rows (rows="3"); use 4–6 only for the primary text of a screen (a message composer). Never taller than the viewport.
  • Vertical resize only. Horizontal resize breaks the layout; disable resize entirely (resize="none") when the height is a design constraint.
  • Always inside a Field with a visible label. Use the hint for the limit and the tone expected ("Visible to the client").
  • Width comes from the layout (100% of the Field). Set max-width on the Field, never a fixed width on the wrapper.
  • Show a character count only when there is a hard limit, as "109 / 240" in the hint. Do not block typing at the limit; mark the field invalid.
  • Sizes match the Inputs around it: md in forms, sm in side panels and comment threads, lg on marketing and auth screens.

Accessibility

  • Every textarea has an accessible name: a <label for> from the Field, or aria-label when standalone.
  • aria-invalid="true" and aria-describedby pointing to the Field error id when invalid.
  • Keep the native resize handle keyboard-independent: the height must never be required to read the content (the textarea scrolls).
  • A character limit is announced through the hint text; do not rely on maxlength alone.
Forms

Select .cn-selectedit

Native <select> dressed as the reference select trigger: the Input ring, radius 8 and heights (36 / 40 / 44px), an optional leading icon and a gray-500 chevron at the trailing edge. The dropdown itself is the operating system menu.

Use: Choose one option from a short, known list (5–15 items: owner, plan, status, sort order). Under 5 options use Radio or SegmentedControl; for long or searchable lists, icons or supporting text in the rows, use Combobox; for multiple choices use Checkboxes. Pair with Field for label and hint.

Default40px tall, 12px padding, medium text, 16px gray-500 chevron 12px from the edge.
HTML
<div class="cn-select" data-variant="default" data-size="md" style="max-width:280px"><select class="cn-select__field" aria-label="Owner"><option value="maya" selected>Maya Chen</option><option value="daniel">Daniel Costa</option><option value="sofia">Sofia Almeida</option><option value="lucas">Lucas Ferreira</option><option value="aisha">Aisha Khan</option><option value="noah">Noah Berg</option></select><svg class="cn-select__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div>
Sizes36 / 40 / 44px; the chevron grows to 20px and the padding to 14px on lg.
HTML
<div style="display:flex;flex-direction:column;gap:var(--cn-space-4)"><div class="cn-select" data-variant="default" data-size="sm" style="max-width:280px"><select class="cn-select__field" aria-label="Owner (small)"><option value="maya" selected>Maya Chen</option><option value="daniel">Daniel Costa</option><option value="sofia">Sofia Almeida</option><option value="lucas">Lucas Ferreira</option><option value="aisha">Aisha Khan</option><option value="noah">Noah Berg</option></select><svg class="cn-select__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div><div class="cn-select" data-variant="default" data-size="md" style="max-width:280px"><select class="cn-select__field" aria-label="Owner (medium)"><option value="maya" selected>Maya Chen</option><option value="daniel">Daniel Costa</option><option value="sofia">Sofia Almeida</option><option value="lucas">Lucas Ferreira</option><option value="aisha">Aisha Khan</option><option value="noah">Noah Berg</option></select><svg class="cn-select__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div><div class="cn-select" data-variant="default" data-size="lg" style="max-width:280px"><select class="cn-select__field" aria-label="Owner (large)"><option value="maya" selected>Maya Chen</option><option value="daniel">Daniel Costa</option><option value="sofia">Sofia Almeida</option><option value="lucas">Lucas Ferreira</option><option value="aisha">Aisha Khan</option><option value="noah">Noah Berg</option></select><svg class="cn-select__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div></div>
With leading iconA 20px gray-500 icon at the padding edge; the text starts 8px after it.
HTML
<div class="cn-select" data-variant="default" data-size="md" style="max-width:280px"><svg class="cn-select__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><select class="cn-select__field" aria-label="Assignee"><option value="maya" selected>Maya Chen</option><option value="daniel">Daniel Costa</option><option value="sofia">Sofia Almeida</option><option value="lucas">Lucas Ferreira</option><option value="aisha">Aisha Khan</option><option value="noah">Noah Berg</option></select><svg class="cn-select__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div>
With placeholder optionAn empty-value disabled option renders in the placeholder color at regular weight.
HTML
<div class="cn-select" data-variant="default" data-size="md" style="max-width:280px"><select class="cn-select__field" aria-label="Plan"><option value="" selected disabled>Select a plan</option><option value="starter">Starter — $29/mo</option><option value="team">Team — $99/mo</option><option value="scale">Scale — $299/mo</option></select><svg class="cn-select__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div>
Filled, small (toolbar sort)
HTML
<div class="cn-select" data-variant="filled" data-size="sm" style="max-width:200px"><select class="cn-select__field" aria-label="Sort by"><option value="recent" selected>Sort: most recent</option><option value="name">Sort: name</option><option value="owner">Sort: owner</option></select><svg class="cn-select__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div>
InvalidPair with a Field error such as "Choose a region to continue".
HTML
<div class="cn-select" data-variant="default" data-size="md" style="max-width:280px"><select class="cn-select__field" aria-invalid="true" aria-label="Region"><option value="" selected disabled>Select a region</option><option value="eu">Europe (Frankfurt)</option><option value="us">United States (Oregon)</option><option value="br">South America (São Paulo)</option></select><svg class="cn-select__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div>
Disabled
HTML
<div class="cn-select" data-variant="default" data-size="md" style="max-width:280px"><select class="cn-select__field" disabled aria-label="Currency"><option value="usd" selected>USD — US dollar</option><option value="eur">EUR — Euro</option></select><svg class="cn-select__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div>

Matrix

variant ↓ / size →smmdlg
default
filled
Anatomy, props, states, rules

Anatomy

  • .cn-select <div> — Wrapper that draws the ring, background, radius, shadow-xs and the focus ring (:focus-within). Position: relative for the icon and chevron.
  • .cn-select__icon <svg> · optional — Optional leading icon (16px on sm, 20px on md and lg) absolutely placed at the padding edge in gray-500; the text starts 8px after it. Decorative, pointer-events: none.
  • .cn-select__field <select> — The native <select> with appearance: none. Fills the wrapper; the trailing padding reserves room for the chevron.
  • .cn-select__chevron <svg> — Chevron-down, 16px (20px on lg), gray-500, absolutely placed 10 / 12 / 14px from the trailing edge. Decorative, pointer-events: none.

Props

propvalues (default outlined)meaning
sizesm md lgControl height and its matching padding, font and icon size.
variantdefault filleddefault = white with the gray-300 ring and shadow-xs (the reference trigger; forms); filled = gray-50 fill without ring or shadow until focus (toolbars, table filters, sort controls — no reference equivalent).

States

stateselectormeaning
hover:hover:not([data-disabled])Pointer over the trigger. The reference has no hover change; presets with a distinct border-control-hover strengthen the ring. (native)
focus:focus-withinFocused or open: the inset ring turns to the brand color and the 4px brand focus ring appears. (native :focus-within on the wrapper)
invalid[data-invalid], &:has([aria-invalid="true"])Red-300 ring; red focus ring on focus. Always accompanied by a Field error. (aria-invalid="true" on the select, or data-invalid on the wrapper)
disabled[data-disabled], &:has(select:disabled)Whole control at 50% opacity, cursor not-allowed. Scoped to the <select> so a disabled placeholder <option> does not trigger it. (disabled on the select)

Rules

  • Use for 5–15 known options. Fewer: Radio or SegmentedControl (all options visible). More, or user-generated lists: Combobox with search.
  • The first option is either a real default or a disabled placeholder with value="" ("Select a plan"); never "Select…" as a selectable option.
  • Option labels are nouns in sentence case, 1–4 words; put the unit or price after an em dash ("Team — $99/mo").
  • Always inside a Field with a visible label, except sort/filter selects in a toolbar (variant="filled", size="sm") whose label is in the option text ("Sort: most recent").
  • Width comes from the layout; set max-width on the Field for short values (currency, region).
  • Sizes: md (40px) in forms; sm (36px) in toolbars and tables; lg (44px) on marketing and auth screens. Never change the height with padding.
  • Keep the native <select>. If the menu needs search, avatars, supporting text or groups with headers, use Combobox.

Accessibility

  • Every select has an accessible name: a <label for> from the Field or aria-label for toolbar selects.
  • Use <optgroup label> for grouped options instead of fake heading options.
  • aria-invalid="true" plus aria-describedby pointing at the Field error id when invalid.
  • The icon and chevron are aria-hidden and pointer-events: none; the select itself receives the click and the keyboard (Alt+Down / typing to jump).
Forms

Combobox .cn-comboboxedit

Text input that filters a list: an Input-ringed field with a trailing chevron and the reference popover listbox under it (radius 8, shadow-lg, 1px ring, 4px padding) whose rows are 36 / 40 / 44px with a gray-50 highlight and a brand check on the selected one.

Use: Choose one item from a long, searchable or user-generated list (people, workspaces, countries), or when rows need an avatar, icon or supporting text. For 5–15 plain options use Select; for free text with suggestions keep the Input; for multiple picks use a TagInput. Pair with Field for the label.

Closed (default)The Input ring, radius 8 and shadow-xs; a 16px gray-500 chevron 12px from the edge.
    HTML
    <div class="cn-combobox" data-size="md" data-state="closed" style="max-width:320px"><input class="cn-combobox__input" type="text" role="combobox" aria-autocomplete="list" aria-controls="cb-assignee-list" aria-expanded="false" placeholder="Search people…" aria-label="Assignee"><svg class="cn-combobox__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg><ul class="cn-combobox__listbox" role="listbox" id="cb-assignee-list"></ul></div>
    Open with optionsMaya is the current value (gray-50 fill and brand check), Daniel is highlighted by the keyboard, Aisha is disabled. Rows are 40px with 24px avatars and muted supporting text.
    • Maya ChenDesign
    • Daniel CostaProduct
    • Sofia AlmeidaResearch
    • Lucas FerreiraEngineering
    • Aisha KhanOn leave
    HTML
    <div class="cn-combobox" data-size="md" data-state="open" style="max-width:320px"><input class="cn-combobox__input" type="text" role="combobox" aria-autocomplete="list" aria-controls="cb-owner-list" aria-expanded="true" value="a" aria-label="Assignee" aria-activedescendant="cb-owner-daniel"><svg class="cn-combobox__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg><ul class="cn-combobox__listbox" role="listbox" id="cb-owner-list"><li class="cn-combobox__option" role="option" id="cb-owner-maya" aria-selected="true"><img class="cn-combobox__option-avatar" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="">Maya Chen<span class="cn-combobox__option-supporting">Design</span><svg class="cn-combobox__option-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></li><li class="cn-combobox__option" role="option" id="cb-owner-daniel" aria-selected="false" data-highlighted><img class="cn-combobox__option-avatar" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="">Daniel Costa<span class="cn-combobox__option-supporting">Product</span><svg class="cn-combobox__option-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></li><li class="cn-combobox__option" role="option" id="cb-owner-sofia" aria-selected="false"><img class="cn-combobox__option-avatar" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="">Sofia Almeida<span class="cn-combobox__option-supporting">Research</span><svg class="cn-combobox__option-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></li><li class="cn-combobox__option" role="option" id="cb-owner-lucas" aria-selected="false"><img class="cn-combobox__option-avatar" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="">Lucas Ferreira<span class="cn-combobox__option-supporting">Engineering</span><svg class="cn-combobox__option-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></li><li class="cn-combobox__option" role="option" id="cb-owner-aisha" aria-selected="false" aria-disabled="true"><img class="cn-combobox__option-avatar" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="">Aisha Khan<span class="cn-combobox__option-supporting">On leave</span><svg class="cn-combobox__option-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></li></ul></div>
    With leading icon and icons in rows
    • Inbox
    • Calendar
    • People
    HTML
    <div class="cn-combobox" data-size="md" data-state="open" style="max-width:320px"><svg class="cn-combobox__leading-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-combobox__input" type="text" role="combobox" aria-autocomplete="list" aria-controls="cb-view-list" aria-expanded="true" value="" placeholder="Choose a view" aria-label="View"><svg class="cn-combobox__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg><ul class="cn-combobox__listbox" role="listbox" id="cb-view-list"><li class="cn-combobox__option" role="option" id="cb-view-inbox" aria-selected="true"><svg class="cn-combobox__option-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg>Inbox<svg class="cn-combobox__option-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></li><li class="cn-combobox__option" role="option" id="cb-view-cal" aria-selected="false"><svg class="cn-combobox__option-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg>Calendar<svg class="cn-combobox__option-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></li><li class="cn-combobox__option" role="option" id="cb-view-people" aria-selected="false"><svg class="cn-combobox__option-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg>People<svg class="cn-combobox__option-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></li></ul></div>
    Open, no results
    HTML
    <div class="cn-combobox" data-size="md" data-state="open" style="max-width:320px"><input class="cn-combobox__input" type="text" role="combobox" aria-autocomplete="list" aria-controls="cb-workspace-list" aria-expanded="true" value="Zorbo" aria-label="Workspace"><svg class="cn-combobox__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg><ul class="cn-combobox__listbox" role="listbox" id="cb-workspace-list"><li class="cn-combobox__empty" role="presentation">No results for “Zorbo”</li></ul></div>
    SizesField and rows share the height: 36 / 40 / 44px. Text is 16px on lg; the chevron and check grow to 20px.
    • Sofia Almeida
    • Lucas Ferreira
    • Noah Berg
    • Aisha Khan
    HTML
    <div style="display:flex;flex-direction:column;gap:var(--cn-space-4)"><div class="cn-combobox" data-size="sm" data-state="open" style="max-width:280px"><input class="cn-combobox__input" type="text" role="combobox" aria-autocomplete="list" aria-controls="cb-sm-list" aria-expanded="true" value="" placeholder="Small · 36px" aria-label="Small"><svg class="cn-combobox__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg><ul class="cn-combobox__listbox" role="listbox" id="cb-sm-list"><li class="cn-combobox__option" role="option" id="cb-sm-1" aria-selected="true">Sofia Almeida<svg class="cn-combobox__option-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></li><li class="cn-combobox__option" role="option" id="cb-sm-2" aria-selected="false">Lucas Ferreira<svg class="cn-combobox__option-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></li></ul></div><div class="cn-combobox" data-size="lg" data-state="open" style="max-width:360px"><input class="cn-combobox__input" type="text" role="combobox" aria-autocomplete="list" aria-controls="cb-lg-list" aria-expanded="true" value="" placeholder="Large · 44px" aria-label="Large"><svg class="cn-combobox__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg><ul class="cn-combobox__listbox" role="listbox" id="cb-lg-list"><li class="cn-combobox__option" role="option" id="cb-lg-1" aria-selected="true">Noah Berg<svg class="cn-combobox__option-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></li><li class="cn-combobox__option" role="option" id="cb-lg-2" aria-selected="false">Aisha Khan<svg class="cn-combobox__option-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></li></ul></div></div>
    InvalidPair with a Field error such as "Pick a workspace from the list".
      HTML
      <div class="cn-combobox" data-size="md" data-state="closed" style="max-width:320px"><input class="cn-combobox__input" type="text" role="combobox" aria-autocomplete="list" aria-controls="cb-invalid-list" aria-expanded="false" value="Lumen Labs" aria-invalid="true" aria-label="Workspace"><svg class="cn-combobox__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg><ul class="cn-combobox__listbox" role="listbox" id="cb-invalid-list"></ul></div>
      Disabled
        HTML
        <div class="cn-combobox" data-size="md" data-state="closed" style="max-width:320px"><input class="cn-combobox__input" type="text" role="combobox" aria-autocomplete="list" aria-controls="cb-region-list" aria-expanded="false" value="Europe (Frankfurt)" disabled aria-label="Data region"><svg class="cn-combobox__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg><ul class="cn-combobox__listbox" role="listbox" id="cb-region-list"></ul></div>

        Matrix

        size
        sm
          md
            lg
              Anatomy, props, states, rules

              Anatomy

              • .cn-combobox <div> — Wrapper (position: relative) that holds the input, the chevron and the listbox. Carries data-state="open|closed", data-invalid, data-disabled.
              • .cn-combobox__leading-icon <svg> · optional — Optional leading icon (16px on sm, 20px on md and lg) at the padding edge in gray-500; the text starts 8px after it. Decorative.
              • .cn-combobox__input <input> — The text field with role="combobox": the Input ring, radius 8, shadow-xs; the trailing padding reserves room for the chevron.
              • .cn-combobox__icon <svg> — Chevron-down, 16px (20px on lg), gray-500, absolutely placed 10 / 12 / 14px from the trailing edge; rotates 180° when open. Decorative, pointer-events: none.
              • .cn-combobox__listbox <ul> — The popover: white, radius 8, shadow-lg with a 1px gray-200 ring, 4px vertical padding (4–6px horizontal), 4px under the input, scrolls after 224 / 256 / 320px. Hidden unless data-state="open".
              • .cn-combobox__option <li> — One row: 36 / 40 / 44px, radius 6, padding 8px 10px 8px 8px, 8px gap, medium label. role="option" with a unique id, aria-selected and optional data-highlighted / aria-disabled.
              • .cn-combobox__option-icon <svg> · optional — Optional leading icon in the row (16px on sm, 20px on md and lg), gray-500.
              • .cn-combobox__option-avatar <img> · optional — Optional round avatar before the label (20px on sm, 24px on md and lg).
              • .cn-combobox__option-supporting <span> · optional — Optional muted text after the label (email, team, price), same size, regular weight.
              • .cn-combobox__option-check <svg> — Check icon (16 / 20px) pushed to the right of the row in the brand color, shown only when aria-selected="true".
              • .cn-combobox__empty <li> · optional — Muted "No results" row rendered instead of options when the filter matches nothing.

              Props

              propvalues (default outlined)meaning
              sizesm md lgControl height and its matching padding, font and icon size.

              States

              stateselectormeaning
              hover:hover:not([data-disabled])Pointer over the field. The reference has no hover change; presets with a distinct border-control-hover strengthen the ring. (native)
              focus:focus-withinThe inset ring turns to the brand color and the 4px brand focus ring appears. Focus never leaves the input while the list is open. (native :focus-within on the root)
              open[data-state="open"]The listbox is visible under the input and the chevron points up. (data-state="open" on the root and aria-expanded="true" on the input)
              invalid[data-invalid], &:has([aria-invalid="true"])Red-300 ring; red focus ring on focus. Always accompanied by a Field error. (aria-invalid="true" on the input, or data-invalid on the root)
              disabled[data-disabled], &:has(input:disabled)Whole control at 50% opacity, cursor not-allowed. (disabled on the input)
              highlighted[data-highlighted]On the __option (not the root): the row under the keyboard cursor or pointer gets the gray-50 fill. Exactly one at a time. (data-highlighted on the option; mirror it with aria-activedescendant on the input)
              selected[aria-selected="true"]On the __option: the current value, gray-50 fill with the brand check at the end. (aria-selected="true" on the option)
              option-disabled[aria-disabled="true"]On the __option: not selectable, 50% opacity, cursor not-allowed, skipped by the keyboard. (aria-disabled="true" on the option)

              Rules

              • Use a Combobox when the list is long (more than 15), user-generated, needs search, or when rows need an avatar, icon or supporting text; otherwise a Select. Typing filters the list; the value is always one of the options.
              • The listbox renders in the flow 4px under the input inside the root. Toggle data-state="open|closed" on the root; the app moves it to a popper layer only when it would be clipped.
              • Rows are 36 / 40 / 44px (same as the field) with radius 6 and one line of text. Show at most 5–7 before scrolling (224 / 256 / 320px).
              • Exactly one option is highlighted at a time (data-highlighted) and follows keyboard and pointer; the selected one has aria-selected="true", the gray-50 fill and the brand check on the right.
              • When nothing matches, render the empty row ("No results for “…”"), never an empty panel and never close the list.
              • The chevron rotates when open. An optional clear (×) button goes before the chevron and appears only when there is a value.
              • Placeholder is an instruction with an ellipsis ("Search people…"); the label lives in the Field.
              • Never use a Combobox for multiple picks; that is a TagInput.

              Accessibility

              • The input has role="combobox", aria-expanded, aria-controls="<listbox id>", aria-autocomplete="list" and aria-activedescendant pointing at the highlighted option id.
              • The list has role="listbox"; each option has role="option", a unique id and aria-selected. Disabled options use aria-disabled="true" and are skipped by the keyboard.
              • Keyboard: Down/Up move the highlight, Enter selects, Escape closes and restores the previous value, Home/End jump. Focus stays on the input the whole time.
              • Announce the result count in a visually hidden live region ("4 results") whenever the filtered list changes.
              • When invalid, aria-invalid="true" on the input and aria-describedby pointing at the Field error id.
              Forms

              Checkbox .cn-checkboxedit

              Yes/no or multi-select choice: a 16px (sm) or 20px (md) box with a gray-300 ring that fills with the brand color and shows a white check, next to a medium label and a muted supporting line. The native input stays in the DOM (invisible but focusable) so every state is CSS-only via :has().

              Use: Independent options that take effect on save (permissions, filters, consent) and multi-select lists. For one-of-many use Radio; for a setting that applies immediately use Switch. Always with a visible label; never a lone box. The card variant turns each option into a selectable bordered card.

              Default16px box, radius 4, gray-300 ring; 14px medium label 8px away.
              HTML
              <label class="cn-checkbox" data-variant="default" data-size="sm"><input class="cn-checkbox__input" type="checkbox" ><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.6666 3.5L5.24992 9.91667L2.33325 7"/></svg></span><span class="cn-checkbox__label">Send me a weekly digest</span></label>
              CheckedBrand-600 fill with a 12px white check.
              HTML
              <label class="cn-checkbox" data-variant="default" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.6666 3.5L5.24992 9.91667L2.33325 7"/></svg></span><span class="cn-checkbox__label">Notify me when Daniel Costa comments</span></label>
              With supporting textThe supporting line is the same size, regular weight, muted.
              HTML
              <label class="cn-checkbox" data-variant="default" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.6666 3.5L5.24992 9.91667L2.33325 7"/></svg></span><span class="cn-checkbox__label">Require two-factor authentication</span><span class="cn-checkbox__description">Members must verify with an authenticator app every 30 days.</span></label>
              Medium20px box, radius 6, 14px check, 16px text 12px away with a 2px gap between lines.
              HTML
              <label class="cn-checkbox" data-variant="default" data-size="md"><input class="cn-checkbox__input" type="checkbox" checked><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.6666 3.5L5.24992 9.91667L2.33325 7"/></svg></span><span class="cn-checkbox__label">Share usage analytics with Lumen</span><span class="cn-checkbox__description">Helps us prioritise what to build next. Never includes your content.</span></label>
              Indeterminate (select all)Set input.indeterminate = true in JS and mirror it with data-indeterminate for CSS.
              HTML
              <label class="cn-checkbox" data-variant="default" data-size="sm" data-indeterminate><input class="cn-checkbox__input" type="checkbox" aria-label="Select all members"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.6666 3.5L5.24992 9.91667L2.33325 7"/></svg></span><span class="cn-checkbox__label">Select all</span><span class="cn-checkbox__description">3 of 8 members selected</span></label>
              InvalidRed-300 ring. Show the error ("Accept the terms to continue") under the group, not inside the label.
              HTML
              <label class="cn-checkbox" data-variant="default" data-size="sm" data-invalid><input class="cn-checkbox__input" type="checkbox" aria-invalid="true" required><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.6666 3.5L5.24992 9.91667L2.33325 7"/></svg></span><span class="cn-checkbox__label">I agree to the Terms of Service and the Data Processing Agreement</span></label>
              DisabledThe box fades to 50% (gray-100 when unchecked); the text keeps its color.
              HTML
              <div style="display:flex;flex-direction:column;gap:var(--cn-space-3)"><label class="cn-checkbox" data-variant="default" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked disabled><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.6666 3.5L5.24992 9.91667L2.33325 7"/></svg></span><span class="cn-checkbox__label">Unlimited seats</span><span class="cn-checkbox__description">Included in the Scale plan.</span></label><label class="cn-checkbox" data-variant="default" data-size="sm"><input class="cn-checkbox__input" type="checkbox" disabled><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.6666 3.5L5.24992 9.91667L2.33325 7"/></svg></span><span class="cn-checkbox__label">Dedicated IP address</span></label></div>
              CardA selectable card: 1px gray-200 ring, radius 12, 16px padding; a 2px brand ring when checked.
              HTML
              <div style="display:flex;flex-direction:column;gap:var(--cn-space-3)"><label class="cn-checkbox" data-variant="card" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.6666 3.5L5.24992 9.91667L2.33325 7"/></svg></span><span class="cn-checkbox__label">Research</span><span class="cn-checkbox__description">Interviews, notes and evidence for Sofia Almeida’s team.</span></label><label class="cn-checkbox" data-variant="card" data-size="sm"><input class="cn-checkbox__input" type="checkbox" ><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.6666 3.5L5.24992 9.91667L2.33325 7"/></svg></span><span class="cn-checkbox__label">Design</span><span class="cn-checkbox__description">Files, prototypes and the review queue.</span></label></div>

              Recipes

              Permission groupA label names the group; options stack 12px apart; the locked one stays visible but disabled.
              Member permissions
              HTML
              <div role="group" aria-labelledby="perm-label" style="display:flex;flex-direction:column;gap:var(--cn-space-3);max-width:420px"><div class="cn-text-label-sm" id="perm-label">Member permissions</div><label class="cn-checkbox" data-variant="default" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.6666 3.5L5.24992 9.91667L2.33325 7"/></svg></span><span class="cn-checkbox__label">View projects and conversations</span></label><label class="cn-checkbox" data-variant="default" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.6666 3.5L5.24992 9.91667L2.33325 7"/></svg></span><span class="cn-checkbox__label">Publish updates</span><span class="cn-checkbox__description">Posts go out under the member’s own name.</span></label><label class="cn-checkbox" data-variant="default" data-size="sm"><input class="cn-checkbox__input" type="checkbox" ><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.6666 3.5L5.24992 9.91667L2.33325 7"/></svg></span><span class="cn-checkbox__label">Approve invoices above $10,000</span></label><label class="cn-checkbox" data-variant="default" data-size="sm"><input class="cn-checkbox__input" type="checkbox" disabled><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.6666 3.5L5.24992 9.91667L2.33325 7"/></svg></span><span class="cn-checkbox__label">Manage billing</span><span class="cn-checkbox__description">Owners only.</span></label></div>
              Add-ons as cardsCards stack 12px apart; the whole card is the hit target and shows the focus ring.
              Add-ons
              HTML
              <div role="group" aria-labelledby="addons-label" style="display:flex;flex-direction:column;gap:var(--cn-space-3);max-width:420px"><div class="cn-text-label-sm" id="addons-label">Add-ons</div><label class="cn-checkbox" data-variant="card" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.6666 3.5L5.24992 9.91667L2.33325 7"/></svg></span><span class="cn-checkbox__label">Priority support · $49/mo</span><span class="cn-checkbox__description">A named contact and a 4-hour response time.</span></label><label class="cn-checkbox" data-variant="card" data-size="sm"><input class="cn-checkbox__input" type="checkbox" ><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.6666 3.5L5.24992 9.91667L2.33325 7"/></svg></span><span class="cn-checkbox__label">Audit log export · $19/mo</span><span class="cn-checkbox__description">Stream every event to your SIEM.</span></label><label class="cn-checkbox" data-variant="card" data-size="sm"><input class="cn-checkbox__input" type="checkbox" disabled><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.6666 3.5L5.24992 9.91667L2.33325 7"/></svg></span><span class="cn-checkbox__label">SSO enforcement</span><span class="cn-checkbox__description">Included in the Enterprise plan.</span></label></div>

              Matrix

              variant ↓ / size →smmd
              default
              card
              Anatomy, props, states, rules

              Anatomy

              • .cn-checkbox <label> — The <label> wrapping everything: it is the hit target and gives the input its name. inline-grid: box in column 1, text in column 2, 8px (sm) or 12px (md) apart.
              • .cn-checkbox__input <input> — Native <input type="checkbox">, visually hidden (opacity 0, positioned over the box) but focusable. Carries checked, disabled, aria-invalid.
              • .cn-checkbox__control <span> — The visible box: white with a 1px gray-300 inset ring, radius 4 (sm) or 6 (md), nudged 2px down to align with the first text line. aria-hidden; purely decorative.
              • .cn-checkbox__indicator <svg> — The white check (12px on sm, 14px on md) inside the box. Hidden until checked; replaced by a dash when indeterminate.
              • .cn-checkbox__label <span> — 14px (sm) or 16px (md) medium text in the default ink. Sentence case, no period, states the positive outcome.
              • .cn-checkbox__description <span> · optional — Optional supporting line under the label, same size, regular weight, muted: consequence or scope of the option.

              Props

              propvalues (default outlined)meaning
              sizesm mdsm = 16px box (radius 4) with a 14px label: forms, settings, tables (the reference default); md = 20px box (radius 6) with a 16px label: touch screens, onboarding and prominent options.
              variantdefault carddefault = bare box and text; card = the whole option is a selectable card (white, 1px gray-200 ring, radius 12, 16px padding) whose ring turns into a 2px brand ring when checked. Use cards for plans, roles and add-ons where the description matters.

              States

              stateselectormeaning
              checked:has(:checked)Box fills with the brand color and shows the white check. A card gets a 2px brand ring. (checked attribute on the input)
              indeterminate[data-indeterminate], &:has(:indeterminate)Partial selection (a "Select all" over a mixed group): brand fill with a dash instead of the check. (data-indeterminate on the root, plus input.indeterminate = true in JS)
              invalid[data-invalid], &:has([aria-invalid="true"])Red-300 ring on the box, for a required acceptance that was skipped. Pair with a visible error message next to the group. (aria-invalid="true" on the input, or data-invalid on the root)
              focus:has(:focus-visible)Keyboard focus on the hidden input shows the 4px brand ring on the box (on the whole card in the card variant). (native :focus-visible on the input)
              disabled[data-disabled], &:has(:disabled)Box at 50% opacity (gray-100 fill when unchecked), cursor not-allowed on the whole row; the text keeps its color. A checked disabled box keeps its check. (disabled on the input)

              Rules

              • Use a checkbox for options that take effect on save. For an immediate effect use Switch; for one-of-many use Radio.
              • The label states the positive outcome ("Send me a weekly digest"), never a negation ("Do not send…") and never an "Enable/Disable" prefix.
              • Labels are sentence case without a period; the supporting line is one full sentence with a period.
              • Stack checkboxes vertically 12px apart (16px between cards). Never place more than two side by side.
              • Indeterminate is only for a parent that summarizes children ("Select all"). Set input.indeterminate in JS and mirror it with data-indeterminate; clicking it checks everything.
              • Invalid is reserved for a required acceptance (terms, consent). Put the error message under the group, in fg-danger, and clear it as soon as the box is checked.
              • sm (16px) in forms, settings and tables; md (20px) on touch screens, onboarding and prominent options. Cards for plans, roles and add-ons where the description matters.
              • Keep the native input in the DOM. The box is decorative (aria-hidden); never rebuild the checkbox with a div and onClick.

              Accessibility

              • The root is a <label> wrapping the input, so the text is the accessible name and the whole row (or card) is the hit target.
              • The visible box is aria-hidden; checked, focus and disabled come from the native input via :has(), so assistive tech sees a normal checkbox.
              • A native checkbox announces "mixed" on its own once input.indeterminate is true; do not add aria-checked to it.
              • Groups get role="group" (or a <fieldset>) with aria-labelledby pointing at the group label.
              • Keep descriptions inside the label; if they contain links or run long, move them out and connect them with aria-describedby.
              Forms

              Radio .cn-radioedit

              One-of-many choice: a 16px (sm) or 20px (md) circle with a gray-300 ring that fills with the brand color and shows a white dot, next to a medium label and a muted supporting line. Same anatomy as Checkbox; always used in a named group of 2–5 options.

              Use: Pick exactly one option when all options should be visible and compared (billing cycle, priority, delivery method). For more than 5 options use Select; for an on/off setting use Switch; for a view toggle in a toolbar use SegmentedControl. The card variant turns each option into a selectable bordered card.

              Default (unchecked)16px circle, gray-300 ring; 14px medium label 8px away.
              HTML
              <label class="cn-radio" data-variant="default" data-size="sm"><input class="cn-radio__input" type="radio" name="ex-cycle" value="monthly"><span class="cn-radio__control" aria-hidden="true"><span class="cn-radio__indicator"></span></span><span class="cn-radio__label">Monthly</span></label>
              CheckedBrand-600 fill with a 6px white dot.
              HTML
              <label class="cn-radio" data-variant="default" data-size="sm"><input class="cn-radio__input" type="radio" name="ex-checked" value="annual" checked><span class="cn-radio__control" aria-hidden="true"><span class="cn-radio__indicator"></span></span><span class="cn-radio__label">Annual</span></label>
              With supporting textThe supporting line is the same size, regular weight, muted.
              HTML
              <label class="cn-radio" data-variant="default" data-size="sm"><input class="cn-radio__input" type="radio" name="ex-desc" value="team" checked><span class="cn-radio__control" aria-hidden="true"><span class="cn-radio__indicator"></span></span><span class="cn-radio__label">Team</span><span class="cn-radio__description">$99 per seat, billed monthly.</span></label>
              Medium20px circle with an 8px dot; 16px text 12px away with a 2px gap between lines.
              HTML
              <label class="cn-radio" data-variant="default" data-size="md"><input class="cn-radio__input" type="radio" name="ex-md" value="scale" checked><span class="cn-radio__control" aria-hidden="true"><span class="cn-radio__indicator"></span></span><span class="cn-radio__label">Scale</span><span class="cn-radio__description">Unlimited seats, SSO and audit log.</span></label>
              InvalidEvery option of the group gets data-invalid; the error message sits under the group.
              HTML
              <label class="cn-radio" data-variant="default" data-size="sm" data-invalid><input class="cn-radio__input" type="radio" name="ex-invalid" value="wire" aria-invalid="true" required><span class="cn-radio__control" aria-hidden="true"><span class="cn-radio__indicator"></span></span><span class="cn-radio__label">Wire transfer</span></label>
              DisabledThe circle fades to 50% (gray-100 when unselected); the text keeps its color.
              HTML
              <div style="display:flex;flex-direction:column;gap:var(--cn-space-4)"><label class="cn-radio" data-variant="default" data-size="sm"><input class="cn-radio__input" type="radio" name="ex-disabled" value="card" checked disabled><span class="cn-radio__control" aria-hidden="true"><span class="cn-radio__indicator"></span></span><span class="cn-radio__label">Company card</span><span class="cn-radio__description">Locked by Aisha Khan, your finance admin.</span></label><label class="cn-radio" data-variant="default" data-size="sm"><input class="cn-radio__input" type="radio" name="ex-disabled" value="invoice" disabled><span class="cn-radio__control" aria-hidden="true"><span class="cn-radio__indicator"></span></span><span class="cn-radio__label">Invoice</span></label></div>
              CardA selectable card: 1px gray-200 ring, radius 12, 16px padding; a 2px brand ring when selected.
              HTML
              <div style="display:flex;flex-direction:column;gap:var(--cn-space-4)"><label class="cn-radio" data-variant="card" data-size="sm"><input class="cn-radio__input" type="radio" name="ex-card" value="starter"><span class="cn-radio__control" aria-hidden="true"><span class="cn-radio__indicator"></span></span><span class="cn-radio__label">Starter — $29/mo</span><span class="cn-radio__description">Up to 5 members and 3 projects.</span></label><label class="cn-radio" data-variant="card" data-size="sm"><input class="cn-radio__input" type="radio" name="ex-card" value="team" checked><span class="cn-radio__control" aria-hidden="true"><span class="cn-radio__indicator"></span></span><span class="cn-radio__label">Team — $99/mo</span><span class="cn-radio__description">Unlimited members, 20 projects and shared libraries.</span></label></div>

              Recipes

              Radio group (stacked)A label names the question; options stack 16px apart; one is always checked.
              Billing cycle
              HTML
              <div class="cn-radio-group" role="radiogroup" aria-labelledby="rg-billing"><div class="cn-radio-group__label" id="rg-billing">Billing cycle</div><label class="cn-radio" data-variant="default" data-size="sm"><input class="cn-radio__input" type="radio" name="billing" value="monthly"><span class="cn-radio__control" aria-hidden="true"><span class="cn-radio__indicator"></span></span><span class="cn-radio__label">Monthly</span><span class="cn-radio__description">$99 per seat, cancel anytime.</span></label><label class="cn-radio" data-variant="default" data-size="sm"><input class="cn-radio__input" type="radio" name="billing" value="annual" checked><span class="cn-radio__control" aria-hidden="true"><span class="cn-radio__indicator"></span></span><span class="cn-radio__label">Annual</span><span class="cn-radio__description">$79 per seat, billed once a year. Save 20%.</span></label></div>
              Radio group (horizontal)Horizontal only for short one-word options without descriptions.
              Priority
              HTML
              <div class="cn-radio-group" role="radiogroup" aria-labelledby="rg-priority" data-orientation="horizontal"><div class="cn-radio-group__label" id="rg-priority">Priority</div><label class="cn-radio" data-variant="default" data-size="sm"><input class="cn-radio__input" type="radio" name="priority" value="low"><span class="cn-radio__control" aria-hidden="true"><span class="cn-radio__indicator"></span></span><span class="cn-radio__label">Low</span></label><label class="cn-radio" data-variant="default" data-size="sm"><input class="cn-radio__input" type="radio" name="priority" value="normal" checked><span class="cn-radio__control" aria-hidden="true"><span class="cn-radio__indicator"></span></span><span class="cn-radio__label">Normal</span></label><label class="cn-radio" data-variant="default" data-size="sm"><input class="cn-radio__input" type="radio" name="priority" value="high"><span class="cn-radio__control" aria-hidden="true"><span class="cn-radio__indicator"></span></span><span class="cn-radio__label">High</span></label></div>
              Plans as cardsCards stack 16px apart; the whole card is the hit target and shows the focus ring.
              Plan
              HTML
              <div class="cn-radio-group" role="radiogroup" aria-labelledby="rg-plan" style="max-width:420px"><div class="cn-radio-group__label" id="rg-plan">Plan</div><label class="cn-radio" data-variant="card" data-size="sm"><input class="cn-radio__input" type="radio" name="plan" value="starter"><span class="cn-radio__control" aria-hidden="true"><span class="cn-radio__indicator"></span></span><span class="cn-radio__label">Starter — $29/mo</span><span class="cn-radio__description">Up to 5 members and 3 projects.</span></label><label class="cn-radio" data-variant="card" data-size="sm"><input class="cn-radio__input" type="radio" name="plan" value="team" checked><span class="cn-radio__control" aria-hidden="true"><span class="cn-radio__indicator"></span></span><span class="cn-radio__label">Team — $99/mo</span><span class="cn-radio__description">Unlimited members, 20 projects and shared libraries.</span></label><label class="cn-radio" data-variant="card" data-size="sm"><input class="cn-radio__input" type="radio" name="plan" value="enterprise"><span class="cn-radio__control" aria-hidden="true"><span class="cn-radio__indicator"></span></span><span class="cn-radio__label">Enterprise</span><span class="cn-radio__description">SSO, audit log and a named contact. Talk to Noah Berg.</span></label></div>

              Matrix

              variant ↓ / size →smmd
              default
              card
              Anatomy, props, states, rules

              Anatomy

              • .cn-radio <label> — The <label> wrapping everything: hit target and accessible name. inline-grid: circle in column 1, text in column 2, 8px (sm) or 12px (md) apart.
              • .cn-radio__input <input> — Native <input type="radio" name="…">, visually hidden but focusable. Options of one question share the same name.
              • .cn-radio__control <span> — The visible circle: white with a 1px gray-300 inset ring, radius full, nudged 2px down to align with the first text line. aria-hidden.
              • .cn-radio__indicator <span> — The white inner dot (6px on sm, 8px on md). Hidden until checked.
              • .cn-radio__label <span> — 14px (sm) or 16px (md) medium text in the default ink. A noun phrase in sentence case, no period.
              • .cn-radio__description <span> · optional — Optional supporting line under the label, same size, regular weight, muted: price, consequence or example.

              Props

              propvalues (default outlined)meaning
              sizesm mdsm = 16px circle with a 14px label: forms, dialogs, tables (the reference default); md = 20px circle with a 16px label: touch screens, onboarding and prominent choices.
              variantdefault carddefault = bare circle and text; card = the whole option is a selectable card (white, 1px gray-200 ring, radius 12, 16px padding) whose ring turns into a 2px brand ring when selected. Use cards for plans and tiers where the description matters.

              States

              stateselectormeaning
              checked:has(:checked)Circle fills with the brand color and shows the white dot. A card gets a 2px brand ring. (checked attribute on the input)
              invalid[data-invalid], &:has([aria-invalid="true"])Red-300 ring on every option of a required group with no selection. Pair with an error message under the group. (aria-invalid="true" on the inputs, or data-invalid on each root)
              focus:has(:focus-visible)Keyboard focus shows the 4px brand ring on the circle (on the whole card in the card variant). (native :focus-visible on the input)
              disabled[data-disabled], &:has(:disabled)Circle at 50% opacity (gray-100 fill when unselected), cursor not-allowed on the whole row; the text keeps its color. A checked disabled option keeps its dot. (disabled on the input)

              Rules

              • A radio group has 2–5 options and always one selected (pre-select the safest or most common). No selection is a validation error, not a state.
              • Wrap options in .cn-radio-group with a label; never a loose row of radios without a visible question.
              • Options are parallel noun phrases in sentence case ("Monthly", "Annual"), never questions or verbs. Put price and consequence in the supporting line.
              • Stacked 16px apart by default; horizontal (data-orientation="horizontal", 24px column gap) only for one-word options without descriptions.
              • If choosing an option reveals more fields, put them directly under that option indented by the label column (8px + circle width), not at the end of the group.
              • Use SegmentedControl instead when the choice is a view/mode in a toolbar; use Select when there are more than 5 options.
              • sm (16px) in forms and dialogs; md (20px) on touch screens and onboarding. Cards for plans and tiers where the description matters.

              Accessibility

              • Each option is a <label> wrapping a native <input type="radio">; every input of the group shares the same name so arrow keys move between them.
              • The group container has role="radiogroup" (or is a <fieldset> with <legend>) and aria-labelledby pointing at the group label.
              • Keyboard: Tab enters the group on the checked option; Up/Down and Left/Right move and select; Space selects when nothing is checked.
              • Invalid: aria-invalid="true" on every input plus aria-describedby pointing at the error under the group.
              • The circle and dot are aria-hidden; state comes from the native input, so no aria-checked is needed.
              Forms

              Switch .cn-switchedit

              On/off toggle that applies immediately: a 36×20 (sm) or 44×24 (md) pill track with a white thumb that slides right while the track turns from gray-100 to the brand color. A slim variant shrinks the track under an overhanging bordered thumb.

              Use: Settings that take effect the moment they change (notifications, dark mode, auto-approve) and feature flags. If the change needs a Save button use Checkbox; if it is a choice between two named options use SegmentedControl or Radio.

              Off36×20 gray-100 track with a hairline ring; 16px white thumb with shadow-sm.
              HTML
              <label class="cn-switch" data-variant="default" data-size="sm"><input class="cn-switch__input" type="checkbox" role="switch" ><span class="cn-switch__track" aria-hidden="true"><span class="cn-switch__thumb"></span></span><span class="cn-switch__label">Email digest</span></label>
              OnBrand-600 track, thumb slid 16px to the right; brand-700 on hover.
              HTML
              <label class="cn-switch" data-variant="default" data-size="sm"><input class="cn-switch__input" type="checkbox" role="switch" checked><span class="cn-switch__track" aria-hidden="true"><span class="cn-switch__thumb"></span></span><span class="cn-switch__label">Desktop notifications</span></label>
              With supporting textSupporting line: same size, regular weight, muted.
              HTML
              <label class="cn-switch" data-variant="default" data-size="sm"><input class="cn-switch__input" type="checkbox" role="switch" checked><span class="cn-switch__track" aria-hidden="true"><span class="cn-switch__thumb"></span></span><span class="cn-switch__label">Auto-approve replies</span><span class="cn-switch__description">Lumen posts follow-ups without waiting for your review when the request only asks for documents.</span></label>
              Medium44×24 track with a 20px thumb; 16px text 12px away.
              HTML
              <label class="cn-switch" data-variant="default" data-size="md"><input class="cn-switch__input" type="checkbox" role="switch" checked><span class="cn-switch__track" aria-hidden="true"><span class="cn-switch__thumb"></span></span><span class="cn-switch__label">Share usage analytics</span><span class="cn-switch__description">Helps us prioritise what to build next.</span></label>
              SlimA thin track under an overhanging bordered thumb: 32×16 (sm) and 40×20 (md). The thumb border turns brand when on.
              HTML
              <div style="display:flex;flex-direction:column;gap:var(--cn-space-4)"><label class="cn-switch" data-variant="slim" data-size="sm"><input class="cn-switch__input" type="checkbox" role="switch" ><span class="cn-switch__track" aria-hidden="true"><span class="cn-switch__thumb"></span></span><span class="cn-switch__label">Show archived</span></label><label class="cn-switch" data-variant="slim" data-size="sm"><input class="cn-switch__input" type="checkbox" role="switch" checked><span class="cn-switch__track" aria-hidden="true"><span class="cn-switch__thumb"></span></span><span class="cn-switch__label">Compact rows</span></label><label class="cn-switch" data-variant="slim" data-size="md"><input class="cn-switch__input" type="checkbox" role="switch" checked><span class="cn-switch__track" aria-hidden="true"><span class="cn-switch__thumb"></span></span><span class="cn-switch__label">Live cursors</span><span class="cn-switch__description">Teammates see where you are editing.</span></label></div>
              DisabledThe track fades to 50%; the text keeps its color.
              HTML
              <div style="display:flex;flex-direction:column;gap:var(--cn-space-4)"><label class="cn-switch" data-variant="default" data-size="sm"><input class="cn-switch__input" type="checkbox" role="switch" checked disabled><span class="cn-switch__track" aria-hidden="true"><span class="cn-switch__thumb"></span></span><span class="cn-switch__label">SSO enforced</span><span class="cn-switch__description">Managed by your identity provider.</span></label><label class="cn-switch" data-variant="default" data-size="sm"><input class="cn-switch__input" type="checkbox" role="switch" disabled><span class="cn-switch__track" aria-hidden="true"><span class="cn-switch__thumb"></span></span><span class="cn-switch__label">Public profile</span></label></div>

              Recipes

              Settings rowsText left, switch right, one row per setting with a hairline between rows. The switch keeps a visually hidden label so it stays named.
              Email digest
              A summary of new comments every weekday at 8:00.
              Desktop notifications
              Only for projects you follow.
              Sounds
              Play a chime when Maya Chen mentions you.
              HTML
              <div style="width:100%;max-width:480px;display:flex;flex-direction:column"><div style="display:flex;align-items:center;justify-content:space-between;gap:var(--cn-space-4);padding:var(--cn-space-4) 0;"><div><div class="cn-text-label-sm">Email digest</div><div class="cn-text-body-md" style="color:var(--cn-color-fg-muted)">A summary of new comments every weekday at 8:00.</div></div><label class="cn-switch" data-variant="default" data-size="sm"><input class="cn-switch__input" type="checkbox" role="switch" checked><span class="cn-switch__track" aria-hidden="true"><span class="cn-switch__thumb"></span></span><span class="cn-switch__label"><span class="cn-sr-only">Email digest</span></span></label></div><div style="display:flex;align-items:center;justify-content:space-between;gap:var(--cn-space-4);padding:var(--cn-space-4) 0;border-top:1px solid var(--cn-color-border-default)"><div><div class="cn-text-label-sm">Desktop notifications</div><div class="cn-text-body-md" style="color:var(--cn-color-fg-muted)">Only for projects you follow.</div></div><label class="cn-switch" data-variant="default" data-size="sm"><input class="cn-switch__input" type="checkbox" role="switch" checked><span class="cn-switch__track" aria-hidden="true"><span class="cn-switch__thumb"></span></span><span class="cn-switch__label"><span class="cn-sr-only">Desktop notifications</span></span></label></div><div style="display:flex;align-items:center;justify-content:space-between;gap:var(--cn-space-4);padding:var(--cn-space-4) 0;border-top:1px solid var(--cn-color-border-default)"><div><div class="cn-text-label-sm">Sounds</div><div class="cn-text-body-md" style="color:var(--cn-color-fg-muted)">Play a chime when Maya Chen mentions you.</div></div><label class="cn-switch" data-variant="default" data-size="sm"><input class="cn-switch__input" type="checkbox" role="switch" ><span class="cn-switch__track" aria-hidden="true"><span class="cn-switch__thumb"></span></span><span class="cn-switch__label"><span class="cn-sr-only">Sounds</span></span></label></div></div>

              Matrix

              variant ↓ / size →smmd
              default
              slim
              Anatomy, props, states, rules

              Anatomy

              • .cn-switch <label> — The <label> wrapping everything: hit target and accessible name. inline-grid: track in column 1, text in column 2, 8px (sm) or 12px (md) apart.
              • .cn-switch__input <input> — Native <input type="checkbox" role="switch">, visually hidden (opacity 0 over the track) but focusable.
              • .cn-switch__track <span> — The pill: 36×20 (sm) or 44×24 (md) with a 2px inset, radius full, gray-100 with a hairline ring when off, brand when on. Slim: 32×16 / 40×20 with no inset. aria-hidden.
              • .cn-switch__thumb <span> — White circle (16px sm / 20px md) with shadow-sm that translates by its own width when on. Slim: shadow-xs and a 1px gray-300 border that turns brand when on.
              • .cn-switch__label <span> — 14px (sm) or 16px (md) medium text in the default ink. Names the thing being turned on ("Email digest").
              • .cn-switch__description <span> · optional — Optional supporting line under the label, same size, regular weight, muted: what happens when it is on.

              Props

              propvalues (default outlined)meaning
              sizesm mdsm = 36×20 track with a 16px thumb and a 14px label: settings rows, tables, popovers (the reference default); md = 44×24 track with a 20px thumb and a 16px label: touch screens and prominent settings.
              variantdefault slimdefault = the thumb sits inside the track with a 2px inset; slim = a thinner track (32×16 / 40×20) with the bordered thumb overhanging it, for dense rows and table cells.

              States

              stateselectormeaning
              hover:hoverWhen on, the track takes the brand hover color (slim: the thumb border does). Off has no hover change. Disabled wins over it. (native :hover on the label)
              checked:has(:checked)On: the track turns to the brand color and the thumb slides right by its own width. (checked attribute on the input)
              focus:has(:focus-visible)Keyboard focus shows the 4px brand ring around the track. (native :focus-visible on the input)
              disabled[data-disabled], &:has(:disabled)Track at 50% opacity, cursor not-allowed on the whole row; the text keeps its color and the on/off position stays readable. (disabled on the input)

              Rules

              • A switch applies immediately; there is no Save button after it. If the change needs a submit, use a Checkbox.
              • The label names the thing being turned on ("Email digest"), not the action ("Enable email digest") and not a question.
              • Never use a switch for a choice between two named options (Monthly / Annual): that is a SegmentedControl or a Radio group.
              • In settings lists the text sits left and the switch right, one row per setting, 16px vertical padding and a hairline between rows.
              • Do not add "On" / "Off" text next to the track; position and color are the state. Confirm with a toast only when the change has side effects (emails sent, data deleted).
              • sm (36×20) everywhere by default; md (44×24) on touch screens and prominent settings; slim inside dense rows and table cells.
              • Motion: thumb and track change together in 150ms; nothing else animates.

              Accessibility

              • The input is a native checkbox with role="switch": screen readers announce on/off and Space toggles it.
              • The root <label> gives the accessible name. When the text lives elsewhere (settings row), keep a cn-sr-only label inside or use aria-labelledby.
              • The focus ring appears on the track through :has(:focus-visible); never remove it.
              • Disabled switches stay readable (track at 50%, text unchanged); say in the supporting line why the setting is locked.
              Forms

              Slider .cn-slideredit

              Native range input with an 8px gray-200 rail, a brand fill up to the current value and a 24px white thumb with a 2px brand ring and shadow-md. The value can sit at the right, under the thumb or in a floating chip above it.

              Use: Pick a value from a continuous or coarse range where the feel matters more than the exact figure (a threshold, a budget band, opacity, volume). For exact numbers use Input type="number"; for a few discrete options use SegmentedControl or Radio. Pair with Field for the label.

              With value at the right8px gray-200 rail, brand fill, 24px white thumb with a 2px brand ring and shadow-md. Set --cn-slider-fill on the root to (value − min) ÷ (max − min).
              40%
              HTML
              <div class="cn-slider" data-label="right" style="--cn-slider-fill: 40%;max-width:360px" ><input class="cn-slider__field" type="range" id="sl-score" min="0" max="100" step="1" value="40" aria-label="Minimum confidence"><output class="cn-slider__value" for="sl-score">40%</output></div>
              Label under the thumbThe reference "bottom" label: 16px medium, centred under the thumb, 8px below the track.
              65%
              HTML
              <div class="cn-slider" data-label="bottom" style="--cn-slider-fill: 65%;max-width:360px" ><input class="cn-slider__field" type="range" id="sl-bottom" min="0" max="100" step="5" value="65" aria-label="Storage quota"><output class="cn-slider__value" for="sl-bottom">65%</output></div>
              Floating label aboveThe reference "top-floating" label: a white chip with radius 8, shadow-lg and a hairline ring, 12px semibold.
              25%
              HTML
              <div class="cn-slider" data-label="top" style="--cn-slider-fill: 25%;max-width:360px" ><input class="cn-slider__field" type="range" id="sl-top" min="0" max="100" step="1" value="25" aria-label="Opacity"><output class="cn-slider__value" for="sl-top">25%</output></div>
              Without readoutPut the value in the Field label or the captions instead.
              HTML
              <div class="cn-slider" data-label="none" style="--cn-slider-fill: 70%;max-width:360px" ><input class="cn-slider__field" type="range" min="0" max="10" step="1" value="7" aria-label="Notification intensity" aria-valuetext="7 of 10"></div>
              Stepped (budget band)Use step for coarse ranges; format the readout with its unit.
              $20k
              HTML
              <div class="cn-slider" data-label="right" style="--cn-slider-fill: 33.3%;max-width:360px" ><input class="cn-slider__field" type="range" id="sl-budget" min="5000" max="50000" step="5000" value="20000" aria-label="Monthly budget" aria-valuetext="$20,000"><output class="cn-slider__value" for="sl-budget">$20k</output></div>
              DisabledWhole control at 50% opacity.
              60%
              HTML
              <div class="cn-slider" data-label="right" style="--cn-slider-fill: 60%;max-width:360px" data-disabled><input class="cn-slider__field" type="range" id="sl-locked" min="0" max="100" value="60" disabled aria-label="Sending rate"><output class="cn-slider__value" for="sl-locked">60%</output></div>

              Recipes

              In a Field with range captionsLabel from the Field, captions under the track in 12px, hint explains the consequence.
              85%
              50% · more drafts100% · safer

              Drafts below this confidence wait for Sofia Almeida’s approval.

              HTML
              <div class="cn-field" data-layout="vertical" data-size="md" style="max-width:360px"><label class="cn-field__label" for="sl-threshold">Auto-publish confidence</label><div class="cn-field__control"><div class="cn-slider" data-label="right" style="--cn-slider-fill: 70%"><input class="cn-slider__field" id="sl-threshold" type="range" min="50" max="100" step="5" value="85" aria-describedby="sl-threshold-helper"><output class="cn-slider__value" for="sl-threshold">85%</output></div><div class="cn-text-body-sm" style="display:flex;justify-content:space-between;color:var(--cn-color-fg-subtle);margin-top:var(--cn-space-1)"><span>50% · more drafts</span><span>100% · safer</span></div></div><p class="cn-field__helper" id="sl-threshold-helper">Drafts below this confidence wait for Sofia Almeida’s approval.</p></div>

              Matrix

              label
              right
              40%
              bottom
              40%
              top
              40%
              none
              40%
              Anatomy, props, states, rules

              Anatomy

              • .cn-slider <div> — Flex row holding the range input and, optionally, the readout. Carries --cn-slider-fill (the value as a percentage), data-label and data-disabled.
              • .cn-slider__field <input> — The native <input type="range">, 24px tall. Rail and thumb are styled through pseudo-elements; the fill comes from the --cn-slider-fill custom property.
              • .cn-slider__value <output> · optional — Optional readout (<output for="<input id>">): right = 14px medium tabular text beside the track; bottom = 16px medium text centred under the thumb; top = a floating 12px semibold chip above the thumb (white, radius 8, shadow-lg, hairline ring).

              Props

              propvalues (default outlined)meaning
              labelright bottom top noneWhere the value readout sits. right = beside the track (default, tabular so it never jumps); bottom = centred under the thumb (the reference "bottom"); top = a floating chip above the thumb (the reference "top-floating"); none = hidden (put the value in the Field label instead).

              States

              stateselectormeaning
              focus:has(:focus-visible)Keyboard focus: the 4px brand ring around the thumb; the readout turns to the default ink. (native :focus-visible on the input)
              active:has(:active)While dragging: the thumb shows the grabbing cursor. (native :active on the input)
              disabled[data-disabled], &:has(:disabled)Whole control at 50% opacity, cursor not-allowed. (disabled on the input)

              Rules

              • Set --cn-slider-fill on the root (or on the input for label="right") to (value − min) ÷ (max − min) × 100% and update it on every input event. Without it the rail shows no fill and the floating labels sit at the start.
              • Always show the value somewhere: the readout (right, bottom or top), the Field label ("Threshold: 40") or captions. A slider without a number is a toy.
              • Use step for coarse ranges (budget bands, percentages in fives). Never a slider for exact figures (prices, dates, quantities under 20): use Input type="number".
              • The right readout is 14px medium, tabular, at least 40px wide so it does not jump between 9 and 10. Format with the unit ($20k, 85%).
              • Bottom and top labels follow the thumb; leave 32px (bottom) or 40px (top) of room, which the variant reserves with a margin.
              • Width comes from the Field: minimum 160px, ideal 240–360px, never full width on desktop.
              • One slider per row. A two-thumb range is a separate component (RangeSlider), not two sliders side by side.
              • Motion: none on the thumb position (it follows the pointer); only the focus ring transitions (100ms).

              Accessibility

              • Keep the native <input type="range">: it exposes role slider with min, max and the current value for free.
              • Give it an accessible name (Field label with for, or aria-label) and aria-valuetext when the number needs a unit ("$20,000", "7 of 10").
              • Keyboard: arrows move one step, Page Up/Down ten steps, Home/End jump to the ends. Do not override these.
              • The <output for> ties the readout to the input; update its text whenever the value changes.
              • The 4px focus ring on the thumb must stay visible in both themes; never set outline: none without the ring.
              Forms

              FileDropzone .cn-file-dropzoneedit

              Upload target: a white card with a 1px gray-200 ring and radius 12, a centered 40px featured icon, "Click to upload or drag and drop" and a hint of accepted types. The native file input covers the whole zone, so click and drop work without extra JS. Selected files list under the text as bordered cards with progress and a delete button.

              Use: Any file upload in a form or dialog (documents, spreadsheets, a logo). For a single small image next to a preview use size="sm". Not for pasting text or URLs, and not as a full-page drop overlay (that is a separate layer).

              DefaultWhite, 1px gray-200 ring, radius 12, 16×24 padding; 40px modern featured icon; "Click to upload" in the brand link color.
              Click to upload or drag and dropPDF, DOCX or FIG (max. 25 MB)
              HTML
              <div class="cn-file-dropzone" data-size="md" data-state="idle" style="max-width:512px"><input class="cn-file-dropzone__input" type="file" multiple accept=".pdf,.docx,.fig" aria-label="Upload files"><span class="cn-featured-icon" data-theme="modern" data-tone="gray" data-size="md" data-shape="square"><svg class="cn-featured-icon__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 10.5V3M4.5 6.5L8 3l3.5 3.5M2.5 11v1.5a1 1 0 001 1h9a1 1 0 001-1V11"/></svg></span><div class="cn-file-dropzone__text"><span class="cn-file-dropzone__title"><span class="cn-file-dropzone__action">Click to upload</span> or drag and drop</span><span class="cn-file-dropzone__hint">PDF, DOCX or FIG (max. 25 MB)</span></div></div>
              Drag overThe ring becomes 2px in the brand color; nothing else moves.
              Release to uploadPDF, DOCX or FIG (max. 25 MB)
              HTML
              <div class="cn-file-dropzone" data-size="md" data-state="dragover" style="max-width:512px"><input class="cn-file-dropzone__input" type="file" multiple accept=".pdf,.docx,.fig" aria-label="Upload files"><span class="cn-featured-icon" data-theme="modern" data-tone="gray" data-size="md" data-shape="square"><svg class="cn-featured-icon__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 10.5V3M4.5 6.5L8 3l3.5 3.5M2.5 11v1.5a1 1 0 001 1h9a1 1 0 001-1V11"/></svg></span><div class="cn-file-dropzone__text"><span class="cn-file-dropzone__title">Release to upload</span><span class="cn-file-dropzone__hint">PDF, DOCX or FIG (max. 25 MB)</span></div></div>
              With files (complete, uploading, failed)Rows are bordered cards with a 40px file icon, medium name, size · status, an 8px progress bar and a utility delete button; a failed row gets a 2px red ring and "Try again".
              Click to upload or drag and dropPDF, DOCX or FIG (max. 25 MB)
              • brand-guidelines-v3.pdf
                1.2 MBComplete
                100%
              • research-synthesis.fig
                348 KBUploading…
                60%
              • interview-notes-daniel-costa.docx
                2.1 MBFailed
              HTML
              <div class="cn-file-dropzone" data-size="md" data-state="idle" style="max-width:512px"><input class="cn-file-dropzone__input" type="file" multiple accept=".pdf,.docx,.fig" aria-label="Upload files"><span class="cn-featured-icon" data-theme="modern" data-tone="gray" data-size="md" data-shape="square"><svg class="cn-featured-icon__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 10.5V3M4.5 6.5L8 3l3.5 3.5M2.5 11v1.5a1 1 0 001 1h9a1 1 0 001-1V11"/></svg></span><div class="cn-file-dropzone__text"><span class="cn-file-dropzone__title"><span class="cn-file-dropzone__action">Click to upload</span> or drag and drop</span><span class="cn-file-dropzone__hint">PDF, DOCX or FIG (max. 25 MB)</span></div><ul class="cn-file-dropzone__files"><li class="cn-file-dropzone__file" data-state="complete"><svg class="cn-file-dropzone__file-icon" viewBox="0 0 40 40" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11 5h13l9 9v19a2 2 0 01-2 2H11a2 2 0 01-2-2V7a2 2 0 012-2z"/><path d="M24 5v9h9"/></svg><div class="cn-file-dropzone__file-body"><span class="cn-file-dropzone__file-name">brand-guidelines-v3.pdf</span><div class="cn-file-dropzone__file-meta"><span class="cn-file-dropzone__file-size">1.2 MB</span><span class="cn-file-dropzone__file-divider" aria-hidden="true"></span><span class="cn-file-dropzone__file-status"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M5.5 8.25l1.75 1.75 3.5-3.5"/></svg>Complete</span></div><div class="cn-file-dropzone__file-progress"><div class="cn-progress" data-size="lg" data-tone="action" role="progressbar" aria-label="Uploading brand-guidelines-v3.pdf" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"><div class="cn-progress__track"><div class="cn-progress__indicator" style="width:100%"></div></div></div><span class="cn-file-dropzone__file-percent">100%</span></div></div><div class="cn-file-dropzone__file-actions"><button type="button" class="cn-icon-button" data-variant="utility" data-size="xs" data-shape="square" aria-label="Delete brand-guidelines-v3.pdf"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4.5h10M6.5 4.5V3h3v1.5M4.5 4.5l.6 8.5h5.8l.6-8.5"/></svg></button></div></li><li class="cn-file-dropzone__file" data-state="uploading"><svg class="cn-file-dropzone__file-icon" viewBox="0 0 40 40" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11 5h13l9 9v19a2 2 0 01-2 2H11a2 2 0 01-2-2V7a2 2 0 012-2z"/><path d="M24 5v9h9"/></svg><div class="cn-file-dropzone__file-body"><span class="cn-file-dropzone__file-name">research-synthesis.fig</span><div class="cn-file-dropzone__file-meta"><span class="cn-file-dropzone__file-size">348 KB</span><span class="cn-file-dropzone__file-divider" aria-hidden="true"></span><span class="cn-file-dropzone__file-status"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 10.5V3M4.5 6.5L8 3l3.5 3.5M2.5 11v1.5a1 1 0 001 1h9a1 1 0 001-1V11"/></svg>Uploading…</span></div><div class="cn-file-dropzone__file-progress"><div class="cn-progress" data-size="lg" data-tone="action" role="progressbar" aria-label="Uploading research-synthesis.fig" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"><div class="cn-progress__track"><div class="cn-progress__indicator" style="width:60%"></div></div></div><span class="cn-file-dropzone__file-percent">60%</span></div></div><div class="cn-file-dropzone__file-actions"><button type="button" class="cn-icon-button" data-variant="utility" data-size="xs" data-shape="square" aria-label="Delete research-synthesis.fig"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4.5h10M6.5 4.5V3h3v1.5M4.5 4.5l.6 8.5h5.8l.6-8.5"/></svg></button></div></li><li class="cn-file-dropzone__file" data-state="failed"><svg class="cn-file-dropzone__file-icon" viewBox="0 0 40 40" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11 5h13l9 9v19a2 2 0 01-2 2H11a2 2 0 01-2-2V7a2 2 0 012-2z"/><path d="M24 5v9h9"/></svg><div class="cn-file-dropzone__file-body"><span class="cn-file-dropzone__file-name">interview-notes-daniel-costa.docx</span><div class="cn-file-dropzone__file-meta"><span class="cn-file-dropzone__file-size">2.1 MB</span><span class="cn-file-dropzone__file-divider" aria-hidden="true"></span><span class="cn-file-dropzone__file-status"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M6 6l4 4M10 6l-4 4"/></svg>Failed</span></div><div class="cn-file-dropzone__file-retry"><button type="button" class="cn-button" data-variant="danger-link" data-size="sm"><span class="cn-button__label">Try again</span></button></div></div><div class="cn-file-dropzone__file-actions"><button type="button" class="cn-icon-button" data-variant="utility" data-size="xs" data-shape="square" aria-label="Delete interview-notes-daniel-costa.docx"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4.5h10M6.5 4.5V3h3v1.5M4.5 4.5l.6 8.5h5.8l.6-8.5"/></svg></button></div></li></ul></div>
              Small (single image)16px padding all round, for one asset next to its preview.
              Click to upload or drag and dropPNG or JPG, square, up to 2 MB
              HTML
              <div class="cn-file-dropzone" data-size="sm" data-state="idle" style="max-width:280px"><input class="cn-file-dropzone__input" type="file" accept="image/png,image/jpeg" aria-label="Upload logo"><span class="cn-featured-icon" data-theme="modern" data-tone="gray" data-size="md" data-shape="square"><svg class="cn-featured-icon__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 10.5V3M4.5 6.5L8 3l3.5 3.5M2.5 11v1.5a1 1 0 001 1h9a1 1 0 001-1V11"/></svg></span><div class="cn-file-dropzone__text"><span class="cn-file-dropzone__title"><span class="cn-file-dropzone__action">Click to upload</span> or drag and drop</span><span class="cn-file-dropzone__hint">PNG or JPG, square, up to 2 MB</span></div></div>
              InvalidThe hint turns to the danger color and starts with the rejected file.
              Click to upload or drag and droparchive.zip is not accepted. Upload PDF, DOCX or FIG (max. 25 MB).
              HTML
              <div class="cn-file-dropzone" data-size="md" data-state="idle" data-invalid style="max-width:512px"><input class="cn-file-dropzone__input" type="file" multiple accept=".pdf,.docx,.fig" aria-invalid="true" aria-label="Upload files"><span class="cn-featured-icon" data-theme="modern" data-tone="gray" data-size="md" data-shape="square"><svg class="cn-featured-icon__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 10.5V3M4.5 6.5L8 3l3.5 3.5M2.5 11v1.5a1 1 0 001 1h9a1 1 0 001-1V11"/></svg></span><div class="cn-file-dropzone__text"><span class="cn-file-dropzone__title"><span class="cn-file-dropzone__action">Click to upload</span> or drag and drop</span><span class="cn-file-dropzone__hint">archive.zip is not accepted. Upload PDF, DOCX or FIG (max. 25 MB).</span></div></div>
              DisabledGray-50 fill, cursor not-allowed, the featured icon at 50%.
              Click to upload or drag and dropUploads are paused while the project is archived.
              HTML
              <div class="cn-file-dropzone" data-size="md" data-state="idle" style="max-width:512px"><input class="cn-file-dropzone__input" type="file" disabled aria-label="Upload files"><span class="cn-featured-icon" data-theme="modern" data-tone="gray" data-size="md" data-shape="square"><svg class="cn-featured-icon__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 10.5V3M4.5 6.5L8 3l3.5 3.5M2.5 11v1.5a1 1 0 001 1h9a1 1 0 001-1V11"/></svg></span><div class="cn-file-dropzone__text"><span class="cn-file-dropzone__title"><span class="cn-file-dropzone__action">Click to upload</span> or drag and drop</span><span class="cn-file-dropzone__hint">Uploads are paused while the project is archived.</span></div></div>

              Recipes

              In a FieldThe Field label names the input through for/id; the hint says where the files go.
              Click to upload or drag and dropPDF, DOCX or FIG (max. 25 MB)
              • usability-report-q3.pdf
                212 KBComplete
                100%

              Visible to everyone in the Lumen workspace.

              HTML
              <div class="cn-field" data-layout="vertical" data-size="md" style="max-width:512px"><label class="cn-field__label" for="dz-docs">Supporting documents <span class="cn-field__optional">(optional)</span></label><div class="cn-field__control"><div class="cn-file-dropzone" data-size="md" data-state="idle"><input class="cn-file-dropzone__input" type="file" id="dz-docs" multiple accept=".pdf,.docx,.fig" aria-describedby="dz-docs-hint"><span class="cn-featured-icon" data-theme="modern" data-tone="gray" data-size="md" data-shape="square"><svg class="cn-featured-icon__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 10.5V3M4.5 6.5L8 3l3.5 3.5M2.5 11v1.5a1 1 0 001 1h9a1 1 0 001-1V11"/></svg></span><div class="cn-file-dropzone__text"><span class="cn-file-dropzone__title"><span class="cn-file-dropzone__action">Click to upload</span> or drag and drop</span><span class="cn-file-dropzone__hint">PDF, DOCX or FIG (max. 25 MB)</span></div><ul class="cn-file-dropzone__files"><li class="cn-file-dropzone__file" data-state="complete"><svg class="cn-file-dropzone__file-icon" viewBox="0 0 40 40" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11 5h13l9 9v19a2 2 0 01-2 2H11a2 2 0 01-2-2V7a2 2 0 012-2z"/><path d="M24 5v9h9"/></svg><div class="cn-file-dropzone__file-body"><span class="cn-file-dropzone__file-name">usability-report-q3.pdf</span><div class="cn-file-dropzone__file-meta"><span class="cn-file-dropzone__file-size">212 KB</span><span class="cn-file-dropzone__file-divider" aria-hidden="true"></span><span class="cn-file-dropzone__file-status"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M5.5 8.25l1.75 1.75 3.5-3.5"/></svg>Complete</span></div><div class="cn-file-dropzone__file-progress"><div class="cn-progress" data-size="lg" data-tone="action" role="progressbar" aria-label="Uploading usability-report-q3.pdf" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"><div class="cn-progress__track"><div class="cn-progress__indicator" style="width:100%"></div></div></div><span class="cn-file-dropzone__file-percent">100%</span></div></div><div class="cn-file-dropzone__file-actions"><button type="button" class="cn-icon-button" data-variant="utility" data-size="xs" data-shape="square" aria-label="Delete usability-report-q3.pdf"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4.5h10M6.5 4.5V3h3v1.5M4.5 4.5l.6 8.5h5.8l.6-8.5"/></svg></button></div></li></ul></div></div><p class="cn-field__helper" id="dz-docs-hint">Visible to everyone in the Lumen workspace.</p></div>

              Matrix

              size
              sm
              Click to upload or drag and dropPDF, DOCX or FIG (max. 25 MB)
              md
              Click to upload or drag and dropPDF, DOCX or FIG (max. 25 MB)
              Anatomy, props, states, rules

              Anatomy

              • .cn-file-dropzone <div> — The zone: white, 1px gray-200 inset ring, radius 12, padding 16×24 (md) or 16 (sm), centered column with a 12px gap. Carries data-state="idle|dragover", data-invalid, data-disabled.
              • .cn-file-dropzone__input <input> — Native <input type="file"> stretched over the whole zone at opacity 0. Carries accept, multiple, disabled, aria-invalid.
              • .cn-file-dropzone__icon <svg> · optional — The upload-cloud icon. Put it inside a FeaturedIcon (theme modern, tone gray, size md = 40px, radius 8); on its own it renders as a 20px muted icon.
              • .cn-file-dropzone__text <div> — Centered text block: the title line and the hint, 4px apart.
              • .cn-file-dropzone__title <span> — The first line, 14px muted: the action ("Click to upload") followed by "or drag and drop" (or "Release to upload" while dragging).
              • .cn-file-dropzone__action <span> — The brand link-colored words inside the title ("Click to upload"): 14px semibold, underlined when the zone is hovered. Not a button — the input above it takes the click.
              • .cn-file-dropzone__hint <span> — 12px muted line: accepted types and size limit ("SVG, PNG, JPG or GIF (max. 800×400px)"). Turns to the danger color and carries the rejection reason when invalid.
              • .cn-file-dropzone__files <ul> · optional — List of selected files under the text, full width, 12px between rows, raised above the input so its buttons are clickable.
              • .cn-file-dropzone__file <li> · optional — One file card: white, 1px gray-200 ring, radius 12, 16px padding, 12px gap between icon, body and actions. data-state="uploading|complete|failed"; failed rows get a 2px red ring.
              • .cn-file-dropzone__file-icon <svg> · optional — 40×40 file-type icon, muted, does not shrink.
              • .cn-file-dropzone__file-body <div> · optional — Name, meta row, progress or retry stacked; takes the remaining width.
              • .cn-file-dropzone__file-name <span> · optional — The file name, 14px medium, truncated with an ellipsis.
              • .cn-file-dropzone__file-meta <div> · optional — Row 2px under the name: size, a 1×12px divider and the status, 8px apart, 14px muted.
              • .cn-file-dropzone__file-size <span> · optional — Formatted size ("1.2 MB"), 14px muted, tabular.
              • .cn-file-dropzone__file-divider <span> · optional — A 1×12px gray-300 rule between size and status.
              • .cn-file-dropzone__file-status <span> · optional — 16px icon + word, 14px medium, 4px apart: "Uploading…" muted, "Complete" green, "Failed" red (colors follow data-state on the row).
              • .cn-file-dropzone__file-progress <div> · optional — Row 4px under the meta: a Progress bar (size lg = 8px track) with the percentage at the right, 12px apart.
              • .cn-file-dropzone__file-percent <span> · optional — The percentage next to the bar, 14px medium, tabular.
              • .cn-file-dropzone__file-retry <div> · optional — Row 6px under the meta on failed rows holding a danger-link sm "Try again" button.
              • .cn-file-dropzone__file-actions <div> · optional — Top-right slot for the utility xs delete button, pulled 8px into the padding so the icon aligns with the content edge.

              Props

              propvalues (default outlined)meaning
              sizesm mdmd = padding 16×24 with the 40px featured icon (documents in forms and dialogs, the reference dropzone); sm = padding 16 all round for a single avatar, logo or attachment next to a preview.

              States

              stateselectormeaning
              dragover[data-state="dragover"]A file is being dragged over the zone: the ring becomes 2px in the brand color. (data-state="dragover" on the root (set on dragenter, cleared on dragleave/drop))
              hover:hover:not([data-disabled])Pointer over the zone: the action words underline, like a link. Nothing else changes. (native :hover)
              focus:has(> input:focus-visible)Keyboard focus on the file input (not on a row button) shows the 4px brand ring around the zone. (native :focus-visible on the input)
              invalid[data-invalid], &:has([aria-invalid="true"])A rejected file (type or size): the hint turns to the danger color and carries the reason. (aria-invalid="true" on the input, or data-invalid on the root)
              disabled[data-disabled], &:has(> input:disabled)Gray-50 fill, cursor not-allowed, the featured icon at 50%, no drop. Scoped to the file input so a disabled row button does not trigger it. (disabled on the input)
              file-failed[data-state="failed"]On a __file row: 2px red ring, red status, "Try again" instead of the progress bar. (data-state="failed" on the row)

              Rules

              • The whole area is the target: the file input covers the root, so a click anywhere opens the picker and a drop anywhere works. Do not add a separate "Browse" button.
              • Title copy is "Click to upload" (brand link) + "or drag and drop"; while dragging over it may change to "Release to upload". Under 768px the second half may be dropped ("Click to upload and attach files").
              • The hint always states accepted types and the size limit ("PDF, DOCX or FIG (max. 25 MB)"); when invalid it starts with the rejected file and keeps the same constraints in one sentence.
              • Set data-state="dragover" on dragenter and clear it on dragleave and drop. The ring changes in 100ms; nothing pulses.
              • Selected files list as cards 12px apart with name, size · status, an 8px progress bar with the percentage and a utility xs delete button. Failed rows set data-state="failed", get a 2px red ring and a danger-link "Try again".
              • md in forms and dialogs, max-width 512px; sm for one small asset (avatar, logo) placed next to its preview.
              • Never a full-page dropzone inside the app shell; use a page-level drop overlay for that and keep this component in the form.

              Accessibility

              • The native <input type="file"> stays focusable: Tab reaches it, Enter or Space open the picker, and the ring shows on the zone through :has(:focus-visible).
              • Give the input an accessible name that says what to upload ("Upload files"): a Field label with for/id, or aria-label.
              • Use accept and multiple on the input so the OS picker and assistive tech know the constraints; repeat them in the hint.
              • Delete buttons carry aria-label "Delete <file name>"; progress bars carry aria-label and aria-valuenow; announce completion or failure in a live region.
              • Drag and drop is an enhancement: the picker must always work, including on touch devices.
              Forms

              ColorPicker .cn-color-pickeredit

              A compact panel for choosing a color: an 8-column grid of round swatches, and a hex Input with a preview swatch and an eyedropper button. The selected swatch shows an action-colored ring.

              Use: Use for brand colors, labels, tags and chart series where a curated palette is enough. Offer the hex field for exact values and the eyedropper when the browser supports it. Not for full-spectrum editing (a design tool); for a fixed set of 3–6 colors use a Radio group of swatches inline.

              Swatch grid with hex fieldSixteen preset swatches in two rows; Brand 600 is selected and previewed in the hex field. Swatch colors are inline backgrounds using the primitive scale variables.
              HTML
              <div class="cn-color-picker" data-size="md" role="group" aria-label="Color"><div class="cn-color-picker__swatches" role="listbox" aria-label="Preset colors"><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Ink" style="background:var(--cn-neutral-950)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Slate" style="background:var(--cn-neutral-700)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Stone" style="background:var(--cn-neutral-400)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Mist" style="background:var(--cn-neutral-200)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Brand 800" style="background:var(--cn-brand-800)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="true" aria-label="Brand 600" style="background:var(--cn-brand-600)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Brand 400" style="background:var(--cn-brand-400)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Brand 200" style="background:var(--cn-brand-200)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Red 600" style="background:var(--cn-red-600)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Red 400" style="background:var(--cn-red-400)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Amber 500" style="background:var(--cn-amber-500)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Amber 300" style="background:var(--cn-amber-300)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Green 600" style="background:var(--cn-green-600)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Green 400" style="background:var(--cn-green-400)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Blue 600" style="background:var(--cn-blue-600)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Blue 400" style="background:var(--cn-blue-400)"></button></div><div class="cn-color-picker__field"><div class="cn-input" data-variant="default" data-size="sm"><span class="cn-color-picker__swatch" data-preview aria-hidden="true" style="background:var(--cn-brand-600)"></span><input class="cn-input__field" type="text" value="#B4309F" aria-label="Hex color" spellcheck="false" autocapitalize="characters" maxlength="7"></div><button type="button" class="cn-color-picker__eyedropper" aria-label="Pick a color from the screen"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.75 2.25l3 3-1.5 1.5-.75-.75L6 11.5 3.5 12.5 4.5 10l5.5-5.5-.75-.75z"/><path d="M9.25 5.25l1.5 1.5"/></svg></button></div></div>
              Custom value (no swatch match)A typed hex that matches no preset: no swatch is selected, the preview shows the typed color.
              HTML
              <div class="cn-color-picker" data-size="md" role="group" aria-label="Color"><div class="cn-color-picker__swatches" role="listbox" aria-label="Preset colors"><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Ink" style="background:var(--cn-neutral-950)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Slate" style="background:var(--cn-neutral-700)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Stone" style="background:var(--cn-neutral-400)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Mist" style="background:var(--cn-neutral-200)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Brand 800" style="background:var(--cn-brand-800)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Brand 600" style="background:var(--cn-brand-600)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Brand 400" style="background:var(--cn-brand-400)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Brand 200" style="background:var(--cn-brand-200)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Red 600" style="background:var(--cn-red-600)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Red 400" style="background:var(--cn-red-400)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Amber 500" style="background:var(--cn-amber-500)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Amber 300" style="background:var(--cn-amber-300)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Green 600" style="background:var(--cn-green-600)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Green 400" style="background:var(--cn-green-400)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Blue 600" style="background:var(--cn-blue-600)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Blue 400" style="background:var(--cn-blue-400)"></button></div><div class="cn-color-picker__field"><div class="cn-input" data-variant="default" data-size="sm"><span class="cn-color-picker__swatch" data-preview aria-hidden="true" style="background:var(--cn-green-600)"></span><input class="cn-input__field" type="text" value="#1E9E6A" aria-label="Hex color" spellcheck="false" autocapitalize="characters" maxlength="7"></div><button type="button" class="cn-color-picker__eyedropper" aria-label="Pick a color from the screen"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.75 2.25l3 3-1.5 1.5-.75-.75L6 11.5 3.5 12.5 4.5 10l5.5-5.5-.75-.75z"/><path d="M9.25 5.25l1.5 1.5"/></svg></button></div></div>
              Small20px swatches in a 240px panel.
              HTML
              <div class="cn-color-picker" data-size="sm" role="group" aria-label="Color"><div class="cn-color-picker__swatches" role="listbox" aria-label="Preset colors"><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Ink" style="background:var(--cn-neutral-950)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Slate" style="background:var(--cn-neutral-700)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Stone" style="background:var(--cn-neutral-400)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Mist" style="background:var(--cn-neutral-200)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Brand 800" style="background:var(--cn-brand-800)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Brand 600" style="background:var(--cn-brand-600)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Brand 400" style="background:var(--cn-brand-400)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Brand 200" style="background:var(--cn-brand-200)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Red 600" style="background:var(--cn-red-600)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Red 400" style="background:var(--cn-red-400)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Amber 500" style="background:var(--cn-amber-500)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Amber 300" style="background:var(--cn-amber-300)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Green 600" style="background:var(--cn-green-600)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Green 400" style="background:var(--cn-green-400)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="true" aria-label="Blue 600" style="background:var(--cn-blue-600)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Blue 400" style="background:var(--cn-blue-400)"></button></div><div class="cn-color-picker__field"><div class="cn-input" data-variant="default" data-size="sm"><span class="cn-color-picker__swatch" data-preview aria-hidden="true" style="background:var(--cn-blue-600)"></span><input class="cn-input__field" type="text" value="#2563EB" aria-label="Hex color" spellcheck="false" autocapitalize="characters" maxlength="7"></div><button type="button" class="cn-color-picker__eyedropper" aria-label="Pick a color from the screen"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.75 2.25l3 3-1.5 1.5-.75-.75L6 11.5 3.5 12.5 4.5 10l5.5-5.5-.75-.75z"/><path d="M9.25 5.25l1.5 1.5"/></svg></button></div></div>
              Invalid hexThe hex Input takes the invalid state; the message sits under the row.

              Enter 6 hex digits, like #B4309F.

              HTML
              <div class="cn-color-picker" data-size="md" role="group" aria-label="Color"><div class="cn-color-picker__swatches" role="listbox" aria-label="Preset colors"><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Ink" style="background:var(--cn-neutral-950)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Slate" style="background:var(--cn-neutral-700)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Stone" style="background:var(--cn-neutral-400)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Mist" style="background:var(--cn-neutral-200)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Brand 800" style="background:var(--cn-brand-800)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Brand 600" style="background:var(--cn-brand-600)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Brand 400" style="background:var(--cn-brand-400)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Brand 200" style="background:var(--cn-brand-200)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Red 600" style="background:var(--cn-red-600)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Red 400" style="background:var(--cn-red-400)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Amber 500" style="background:var(--cn-amber-500)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Amber 300" style="background:var(--cn-amber-300)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Green 600" style="background:var(--cn-green-600)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Green 400" style="background:var(--cn-green-400)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Blue 600" style="background:var(--cn-blue-600)"></button><button type="button" class="cn-color-picker__swatch" role="option" aria-selected="false" aria-label="Blue 400" style="background:var(--cn-blue-400)"></button></div><div class="cn-color-picker__field"><div class="cn-input" data-variant="default" data-size="sm"><span class="cn-color-picker__swatch" data-preview aria-hidden="true" style="background:var(--cn-neutral-200)"></span><input class="cn-input__field" type="text" value="#12G" aria-label="Hex color" spellcheck="false" autocapitalize="characters" maxlength="7" aria-invalid="true" aria-describedby="hex-error"></div><button type="button" class="cn-color-picker__eyedropper" aria-label="Pick a color from the screen"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.75 2.25l3 3-1.5 1.5-.75-.75L6 11.5 3.5 12.5 4.5 10l5.5-5.5-.75-.75z"/><path d="M9.25 5.25l1.5 1.5"/></svg></button></div><p class="cn-text-body-sm" id="hex-error" style="margin:0;color:var(--cn-color-fg-danger)">Enter 6 hex digits, like #B4309F.</p></div>

              Matrix

              size
              sm
              md
              Anatomy, props, states, rules

              Anatomy

              • .cn-color-picker <div> — The panel (flex column, gap space.3): 280px wide (240 on sm), padding space.4, surface-raised, hairline, radius panel, shadow-lg. role="group" with an aria-label. Carries data-size.
              • .cn-color-picker__swatches <div> — 8-column grid, gap space.2. role="listbox" aria-label "Preset colors".
              • .cn-color-picker__swatch <button> — One 24px round swatch (20px on sm) with a hairline; its color is an inline background. role="option" aria-selected, aria-label naming the color. With data-preview it is the 16px preview inside the hex field.
              • .cn-color-picker__field <div> — Row (flex, gap space.2) with the hex Input (size sm, leading preview swatch) and the eyedropper button.
              • .cn-color-picker__eyedropper <button> — Square outline icon button (control height sm) with a pipette icon. aria-label "Pick a color from the screen". Hide it when the EyeDropper API is unavailable.

              Props

              propvalues (default outlined)meaning
              sizesm mdmd = 24px swatches in a 280px panel (default); sm = 20px swatches in a 240px panel for popovers inside dense settings.

              States

              stateselectormeaning
              selected .cn-color-picker__swatch[aria-selected="true"]On the __swatch: a 2px action-colored ring (box-shadow, offset by the panel surface) marks the current color. Styled in extraCss because it lives on a child. (aria-selected="true" on the swatch)
              swatchHover .cn-color-picker__swatch:hoverOn the __swatch: the hairline strengthens. Styled in extraCss. (native :hover on a swatch)
              swatchFocus .cn-color-picker__swatch:focus-visibleOn the __swatch: the focus ring. Styled in extraCss. (native :focus-visible on a swatch)
              eyedropperHover .cn-color-picker__eyedropper:hoverOn the __eyedropper: subtle fill and default ink. Styled in extraCss. (native :hover on the eyedropper)

              Rules

              • Exactly 8 swatches per row and 1–3 rows (8–24 colors). Order them by hue family, darkest to lightest, neutrals first.
              • The swatch color is the only inline style allowed (style="background:…"), preferably a primitive scale variable so the palette follows the theme.
              • Selection is a ring, not a checkmark or a size change: aria-selected="true" draws the 2px action ring offset by the panel surface.
              • The hex field always mirrors the selection: clicking a swatch writes its hex; typing a valid hex selects a matching swatch or clears the selection.
              • Accept #RGB and #RRGGBB, normalize to uppercase #RRGGBB on blur, and mark the field invalid (with a message) otherwise.
              • Show the eyedropper only when window.EyeDropper exists; never render a disabled eyedropper.
              • The panel is statically positioned by the component; the app anchors it under its trigger (a swatch button or an Input) with an 8px offset and closes it on Escape.
              • One ColorPicker per popover; never inline in a form, where a Radio group of swatches is lighter.

              Accessibility

              • The swatch grid is role="listbox" with role="option" swatches; each has an aria-label naming the color ("Brand 600") and aria-selected. Arrow keys move between swatches, Enter or Space select.
              • The hex input has an accessible name ("Hex color") and aria-invalid plus aria-describedby when the value is not a valid hex.
              • The preview swatch inside the field is aria-hidden; the hex text is the accessible value.
              • The eyedropper is a real <button> with aria-label; it announces the picked color by updating the hex input.
              • Color is never the only cue for selection: the ring and the hex value both change.
              Forms

              DatePicker .cn-date-pickeredit

              A calendar panel: month header with ghost prev/next buttons, a weekday row, a 7-column grid of round day buttons, and a footer with the typed date and Cancel / Apply. Selected days fill with the action color, ranges tint the days between, today carries a dot. An optional presets column offers shortcuts.

              Use: Pick one date or a start–end range: due dates, report periods, filters by period. The panel opens from a Button (calendar icon + the formatted date) or from an Input with a leading calendar icon. For a time or a date-time use an Input with the right type; for recurring rules use a form.

              Single dateSeptember 2026: the 18th is selected, today (the 11th) carries the dot, Aug 31 and Oct 1–4 are outside days.
              HTML
              <div class="cn-date-picker" data-variant="single" data-size="md" role="dialog" aria-label="Choose a date"><div class="cn-date-picker__calendar"><div class="cn-date-picker__header"><button type="button" class="cn-date-picker__nav" aria-label="Previous month"><svg class="cn-icon" style="transform:scaleX(-1)" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button><div class="cn-date-picker__month" aria-live="polite">September 2026</div><button type="button" class="cn-date-picker__nav" aria-label="Next month"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button></div><div class="cn-date-picker__weekdays" aria-hidden="true"><span>Mo</span><span>Tu</span><span>We</span><span>Th</span><span>Fr</span><span>Sa</span><span>Su</span></div><div class="cn-date-picker__grid" role="group" aria-label="September 2026"><button type="button" class="cn-date-picker__day" data-outside>31</button><button type="button" class="cn-date-picker__day">1</button><button type="button" class="cn-date-picker__day">2</button><button type="button" class="cn-date-picker__day">3</button><button type="button" class="cn-date-picker__day">4</button><button type="button" class="cn-date-picker__day">5</button><button type="button" class="cn-date-picker__day">6</button><button type="button" class="cn-date-picker__day">7</button><button type="button" class="cn-date-picker__day">8</button><button type="button" class="cn-date-picker__day">9</button><button type="button" class="cn-date-picker__day">10</button><button type="button" class="cn-date-picker__day" data-today aria-current="date">11</button><button type="button" class="cn-date-picker__day">12</button><button type="button" class="cn-date-picker__day">13</button><button type="button" class="cn-date-picker__day">14</button><button type="button" class="cn-date-picker__day">15</button><button type="button" class="cn-date-picker__day">16</button><button type="button" class="cn-date-picker__day">17</button><button type="button" class="cn-date-picker__day" aria-selected="true">18</button><button type="button" class="cn-date-picker__day">19</button><button type="button" class="cn-date-picker__day">20</button><button type="button" class="cn-date-picker__day">21</button><button type="button" class="cn-date-picker__day">22</button><button type="button" class="cn-date-picker__day">23</button><button type="button" class="cn-date-picker__day">24</button><button type="button" class="cn-date-picker__day">25</button><button type="button" class="cn-date-picker__day">26</button><button type="button" class="cn-date-picker__day">27</button><button type="button" class="cn-date-picker__day">28</button><button type="button" class="cn-date-picker__day">29</button><button type="button" class="cn-date-picker__day">30</button><button type="button" class="cn-date-picker__day" data-outside>1</button><button type="button" class="cn-date-picker__day" data-outside>2</button><button type="button" class="cn-date-picker__day" data-outside>3</button><button type="button" class="cn-date-picker__day" data-outside>4</button></div><div class="cn-date-picker__footer"><div class="cn-input" data-variant="default" data-size="sm"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><input class="cn-input__field" type="text" value="Sep 18, 2026" aria-label="Selected date"></div><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Cancel</span></button><button type="button" class="cn-button" data-variant="primary" data-size="sm"><span class="cn-button__label">Apply</span></button></div></div></div>
              Range 8–14Start and end are filled; the days between take the subtle tint with no radius. Today falls inside the range and keeps its dot.
              HTML
              <div class="cn-date-picker" data-variant="range" data-size="md" role="dialog" aria-label="Choose a date"><div class="cn-date-picker__calendar"><div class="cn-date-picker__header"><button type="button" class="cn-date-picker__nav" aria-label="Previous month"><svg class="cn-icon" style="transform:scaleX(-1)" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button><div class="cn-date-picker__month" aria-live="polite">September 2026</div><button type="button" class="cn-date-picker__nav" aria-label="Next month"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button></div><div class="cn-date-picker__weekdays" aria-hidden="true"><span>Mo</span><span>Tu</span><span>We</span><span>Th</span><span>Fr</span><span>Sa</span><span>Su</span></div><div class="cn-date-picker__grid" role="group" aria-label="September 2026"><button type="button" class="cn-date-picker__day" data-outside>31</button><button type="button" class="cn-date-picker__day">1</button><button type="button" class="cn-date-picker__day">2</button><button type="button" class="cn-date-picker__day">3</button><button type="button" class="cn-date-picker__day">4</button><button type="button" class="cn-date-picker__day">5</button><button type="button" class="cn-date-picker__day">6</button><button type="button" class="cn-date-picker__day">7</button><button type="button" class="cn-date-picker__day" aria-selected="true" data-range="start">8</button><button type="button" class="cn-date-picker__day" data-range="middle">9</button><button type="button" class="cn-date-picker__day" data-range="middle">10</button><button type="button" class="cn-date-picker__day" data-range="middle" data-today aria-current="date">11</button><button type="button" class="cn-date-picker__day" data-range="middle">12</button><button type="button" class="cn-date-picker__day" data-range="middle">13</button><button type="button" class="cn-date-picker__day" aria-selected="true" data-range="end">14</button><button type="button" class="cn-date-picker__day">15</button><button type="button" class="cn-date-picker__day">16</button><button type="button" class="cn-date-picker__day">17</button><button type="button" class="cn-date-picker__day">18</button><button type="button" class="cn-date-picker__day">19</button><button type="button" class="cn-date-picker__day">20</button><button type="button" class="cn-date-picker__day">21</button><button type="button" class="cn-date-picker__day">22</button><button type="button" class="cn-date-picker__day">23</button><button type="button" class="cn-date-picker__day">24</button><button type="button" class="cn-date-picker__day">25</button><button type="button" class="cn-date-picker__day">26</button><button type="button" class="cn-date-picker__day">27</button><button type="button" class="cn-date-picker__day">28</button><button type="button" class="cn-date-picker__day">29</button><button type="button" class="cn-date-picker__day">30</button><button type="button" class="cn-date-picker__day" data-outside>1</button><button type="button" class="cn-date-picker__day" data-outside>2</button><button type="button" class="cn-date-picker__day" data-outside>3</button><button type="button" class="cn-date-picker__day" data-outside>4</button></div><div class="cn-date-picker__footer"><div class="cn-input" data-variant="default" data-size="sm"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><input class="cn-input__field" type="text" value="Sep 8 – Sep 14, 2026" aria-label="Selected range"></div><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Cancel</span></button><button type="button" class="cn-button" data-variant="primary" data-size="sm"><span class="cn-button__label">Apply</span></button></div></div></div>
              With presetsA shortcut column on the left; "Last week" is the active preset and drives the range.
              HTML
              <div class="cn-date-picker" data-variant="with-presets" data-size="md" role="dialog" aria-label="Choose a date"><div class="cn-date-picker__presets"><button type="button" class="cn-date-picker__preset">Today</button><button type="button" class="cn-date-picker__preset">Yesterday</button><button type="button" class="cn-date-picker__preset">This week</button><button type="button" class="cn-date-picker__preset" aria-current="true">Last week</button><button type="button" class="cn-date-picker__preset">This month</button><button type="button" class="cn-date-picker__preset">Last month</button></div><div class="cn-date-picker__calendar"><div class="cn-date-picker__header"><button type="button" class="cn-date-picker__nav" aria-label="Previous month"><svg class="cn-icon" style="transform:scaleX(-1)" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button><div class="cn-date-picker__month" aria-live="polite">September 2026</div><button type="button" class="cn-date-picker__nav" aria-label="Next month"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button></div><div class="cn-date-picker__weekdays" aria-hidden="true"><span>Mo</span><span>Tu</span><span>We</span><span>Th</span><span>Fr</span><span>Sa</span><span>Su</span></div><div class="cn-date-picker__grid" role="group" aria-label="September 2026"><button type="button" class="cn-date-picker__day" data-outside>31</button><button type="button" class="cn-date-picker__day">1</button><button type="button" class="cn-date-picker__day">2</button><button type="button" class="cn-date-picker__day">3</button><button type="button" class="cn-date-picker__day">4</button><button type="button" class="cn-date-picker__day">5</button><button type="button" class="cn-date-picker__day">6</button><button type="button" class="cn-date-picker__day">7</button><button type="button" class="cn-date-picker__day" aria-selected="true" data-range="start">8</button><button type="button" class="cn-date-picker__day" data-range="middle">9</button><button type="button" class="cn-date-picker__day" data-range="middle">10</button><button type="button" class="cn-date-picker__day" data-range="middle" data-today aria-current="date">11</button><button type="button" class="cn-date-picker__day" data-range="middle">12</button><button type="button" class="cn-date-picker__day" data-range="middle">13</button><button type="button" class="cn-date-picker__day" aria-selected="true" data-range="end">14</button><button type="button" class="cn-date-picker__day">15</button><button type="button" class="cn-date-picker__day">16</button><button type="button" class="cn-date-picker__day">17</button><button type="button" class="cn-date-picker__day">18</button><button type="button" class="cn-date-picker__day">19</button><button type="button" class="cn-date-picker__day">20</button><button type="button" class="cn-date-picker__day">21</button><button type="button" class="cn-date-picker__day">22</button><button type="button" class="cn-date-picker__day">23</button><button type="button" class="cn-date-picker__day">24</button><button type="button" class="cn-date-picker__day">25</button><button type="button" class="cn-date-picker__day">26</button><button type="button" class="cn-date-picker__day">27</button><button type="button" class="cn-date-picker__day">28</button><button type="button" class="cn-date-picker__day">29</button><button type="button" class="cn-date-picker__day">30</button><button type="button" class="cn-date-picker__day" data-outside>1</button><button type="button" class="cn-date-picker__day" data-outside>2</button><button type="button" class="cn-date-picker__day" data-outside>3</button><button type="button" class="cn-date-picker__day" data-outside>4</button></div><div class="cn-date-picker__footer"><div class="cn-input" data-variant="default" data-size="sm"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><input class="cn-input__field" type="text" value="Sep 8 – Sep 14, 2026" aria-label="Selected range"></div><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Cancel</span></button><button type="button" class="cn-button" data-variant="primary" data-size="sm"><span class="cn-button__label">Apply</span></button></div></div></div>
              Small36px days, 300px panel, for popovers inside dense toolbars.
              HTML
              <div class="cn-date-picker" data-variant="single" data-size="sm" role="dialog" aria-label="Choose a date"><div class="cn-date-picker__calendar"><div class="cn-date-picker__header"><button type="button" class="cn-date-picker__nav" aria-label="Previous month"><svg class="cn-icon" style="transform:scaleX(-1)" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button><div class="cn-date-picker__month" aria-live="polite">September 2026</div><button type="button" class="cn-date-picker__nav" aria-label="Next month"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button></div><div class="cn-date-picker__weekdays" aria-hidden="true"><span>Mo</span><span>Tu</span><span>We</span><span>Th</span><span>Fr</span><span>Sa</span><span>Su</span></div><div class="cn-date-picker__grid" role="group" aria-label="September 2026"><button type="button" class="cn-date-picker__day" data-outside>31</button><button type="button" class="cn-date-picker__day">1</button><button type="button" class="cn-date-picker__day">2</button><button type="button" class="cn-date-picker__day">3</button><button type="button" class="cn-date-picker__day">4</button><button type="button" class="cn-date-picker__day">5</button><button type="button" class="cn-date-picker__day">6</button><button type="button" class="cn-date-picker__day">7</button><button type="button" class="cn-date-picker__day">8</button><button type="button" class="cn-date-picker__day">9</button><button type="button" class="cn-date-picker__day">10</button><button type="button" class="cn-date-picker__day" aria-selected="true" data-today aria-current="date">11</button><button type="button" class="cn-date-picker__day">12</button><button type="button" class="cn-date-picker__day">13</button><button type="button" class="cn-date-picker__day">14</button><button type="button" class="cn-date-picker__day">15</button><button type="button" class="cn-date-picker__day">16</button><button type="button" class="cn-date-picker__day">17</button><button type="button" class="cn-date-picker__day">18</button><button type="button" class="cn-date-picker__day">19</button><button type="button" class="cn-date-picker__day">20</button><button type="button" class="cn-date-picker__day">21</button><button type="button" class="cn-date-picker__day">22</button><button type="button" class="cn-date-picker__day">23</button><button type="button" class="cn-date-picker__day">24</button><button type="button" class="cn-date-picker__day">25</button><button type="button" class="cn-date-picker__day">26</button><button type="button" class="cn-date-picker__day">27</button><button type="button" class="cn-date-picker__day">28</button><button type="button" class="cn-date-picker__day">29</button><button type="button" class="cn-date-picker__day">30</button><button type="button" class="cn-date-picker__day" data-outside>1</button><button type="button" class="cn-date-picker__day" data-outside>2</button><button type="button" class="cn-date-picker__day" data-outside>3</button><button type="button" class="cn-date-picker__day" data-outside>4</button></div><div class="cn-date-picker__footer"><div class="cn-input" data-variant="default" data-size="sm"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><input class="cn-input__field" type="text" value="Sep 11, 2026" aria-label="Selected date"></div><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Cancel</span></button><button type="button" class="cn-button" data-variant="primary" data-size="sm"><span class="cn-button__label">Apply</span></button></div></div></div>
              Date input (trigger)The panel opens from an Input with a leading calendar icon (forms) or from an outline Button (filters).
              HTML
              <div style="display:flex;flex-wrap:wrap;gap:var(--cn-space-3);align-items:center"><div class="cn-input" data-variant="default" data-size="md" style="max-width:220px"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><input class="cn-input__field" type="text" value="Sep 11, 2026" aria-label="Due date" aria-haspopup="dialog"></div><button type="button" class="cn-button" data-variant="outline" data-size="md" aria-haspopup="dialog"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><span class="cn-button__label">Sep 8 – Sep 14, 2026</span></button></div>
              Disabled daysDays before today are disabled: no hover, disabled ink, still visible so the month reads whole.
              HTML
              <div class="cn-date-picker" data-variant="single" data-size="md" role="dialog" aria-label="Choose a date"><div class="cn-date-picker__calendar"><div class="cn-date-picker__header"><button type="button" class="cn-date-picker__nav" aria-label="Previous month"><svg class="cn-icon" style="transform:scaleX(-1)" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button><div class="cn-date-picker__month" aria-live="polite">September 2026</div><button type="button" class="cn-date-picker__nav" aria-label="Next month"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button></div><div class="cn-date-picker__weekdays" aria-hidden="true"><span>Mo</span><span>Tu</span><span>We</span><span>Th</span><span>Fr</span><span>Sa</span><span>Su</span></div><div class="cn-date-picker__grid" role="group" aria-label="September 2026"><button type="button" class="cn-date-picker__day" data-outside>31</button><button type="button" class="cn-date-picker__day" disabled>1</button><button type="button" class="cn-date-picker__day" disabled>2</button><button type="button" class="cn-date-picker__day" disabled>3</button><button type="button" class="cn-date-picker__day" disabled>4</button><button type="button" class="cn-date-picker__day" disabled>5</button><button type="button" class="cn-date-picker__day" disabled>6</button><button type="button" class="cn-date-picker__day" disabled>7</button><button type="button" class="cn-date-picker__day" disabled>8</button><button type="button" class="cn-date-picker__day" disabled>9</button><button type="button" class="cn-date-picker__day" disabled>10</button><button type="button" class="cn-date-picker__day" data-today aria-current="date">11</button><button type="button" class="cn-date-picker__day">12</button><button type="button" class="cn-date-picker__day">13</button><button type="button" class="cn-date-picker__day">14</button><button type="button" class="cn-date-picker__day" aria-selected="true">15</button><button type="button" class="cn-date-picker__day">16</button><button type="button" class="cn-date-picker__day">17</button><button type="button" class="cn-date-picker__day">18</button><button type="button" class="cn-date-picker__day">19</button><button type="button" class="cn-date-picker__day">20</button><button type="button" class="cn-date-picker__day">21</button><button type="button" class="cn-date-picker__day">22</button><button type="button" class="cn-date-picker__day">23</button><button type="button" class="cn-date-picker__day">24</button><button type="button" class="cn-date-picker__day">25</button><button type="button" class="cn-date-picker__day">26</button><button type="button" class="cn-date-picker__day">27</button><button type="button" class="cn-date-picker__day">28</button><button type="button" class="cn-date-picker__day">29</button><button type="button" class="cn-date-picker__day">30</button><button type="button" class="cn-date-picker__day" data-outside>1</button><button type="button" class="cn-date-picker__day" data-outside>2</button><button type="button" class="cn-date-picker__day" data-outside>3</button><button type="button" class="cn-date-picker__day" data-outside>4</button></div><div class="cn-date-picker__footer"><div class="cn-input" data-variant="default" data-size="sm"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><input class="cn-input__field" type="text" value="Sep 15, 2026" aria-label="Delivery date"></div><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Cancel</span></button><button type="button" class="cn-button" data-variant="primary" data-size="sm"><span class="cn-button__label">Apply</span></button></div></div></div>

              Matrix

              variant ↓ / size →smmd
              single
              range
              with-presets
              Anatomy, props, states, rules

              Anatomy

              • .cn-date-picker <div> — The floating panel (row): surface-raised, hairline, radius panel, shadow-lg. role="dialog" with an aria-label. Holds an optional presets column and the calendar column. Carries data-variant and data-size.
              • .cn-date-picker__presets <div> · optional — Left column of shortcut buttons (Today, Yesterday, This week, Last week, This month, Last month), separated from the calendar by a hairline. Only in the with-presets variant.
              • .cn-date-picker__preset <button> · optional — One shortcut row, 36px, body-sm, radius md; aria-current="true" on the active one.
              • .cn-date-picker__calendar <div> — The calendar column (flex column) wrapping header, weekdays, grid and footer. Width = 7 days + 2 × space.6 (328px on md, 300px on sm).
              • .cn-date-picker__header <div> — Row with the previous button, the month name and the next button.
              • .cn-date-picker__month <div> — The month and year ("September 2026"), label-md, aria-live="polite".
              • .cn-date-picker__nav <button> — 32px ghost icon button with a chevron; aria-label "Previous month" / "Next month".
              • .cn-date-picker__weekdays <div> — 7-column row of two-letter weekday names, label-sm muted, aria-hidden.
              • .cn-date-picker__grid <div> — 7-column grid of day buttons with a 4px row gap. role="group" named after the month (or a real role="grid" with rows when built with JS).
              • .cn-date-picker__day <button> — One day: 40×40 (36 on sm), body-sm, radius full. Attributes: aria-selected="true" (chosen), data-range="start|middle|end", data-today, data-outside (previous/next month), disabled.
              • .cn-date-picker__footer <div> — Top hairline; the typed date in a small Input with a calendar icon, then Cancel (outline) and Apply (primary), size sm.

              Props

              propvalues (default outlined)meaning
              variantsingle range with-presetssingle = one date, one Apply; range = a start and an end day with the days between tinted, the footer input shows "Sep 8 – Sep 14, 2026"; with-presets = a range picker with a left column of shortcuts (Today … Last month) for filters by period.
              sizesm mdmd = 40px days, 328px panel (default); sm = 36px days, 300px panel for popovers inside dense toolbars.

              States

              stateselectormeaning
              dayHover .cn-date-picker__day:hover:not(:disabled):not([aria-selected="true"])On the __day: subtle fill under the pointer. Styled in extraCss because it lives on a child. (native :hover on a day)
              dayFocus .cn-date-picker__day:focus-visibleOn the __day: the focus ring (roving tabindex, arrow keys move it). Styled in extraCss. (native :focus-visible on a day)
              today .cn-date-picker__day[data-today]On the __day: semibold with a 4px action-colored dot under the number. Styled in extraCss. (data-today and aria-current="date" on the day)
              selected .cn-date-picker__day[aria-selected="true"]On the __day: action fill, on-action text, medium weight. The start and end of a range are also selected. Styled in extraCss. (aria-selected="true" on the day)
              range .cn-date-picker__day[data-range]On the __day: days inside a range take the subtle action tint and lose their radius; data-range="start" keeps the left radius, "end" the right one. Styled in extraCss. (data-range="start|middle|end" on the day)
              outside .cn-date-picker__day[data-outside]On the __day: days of the previous/next month in fg-subtle. Styled in extraCss. (data-outside on the day)
              dayDisabled .cn-date-picker__day:disabledOn the __day: outside the allowed period, disabled ink, no hover. Styled in extraCss. (disabled on the day)
              presetCurrent .cn-date-picker__preset[aria-current="true"]On the __preset: the active shortcut, subtle fill and medium weight. Styled in extraCss. (aria-current="true" on the preset)

              Rules

              • Weeks start on Monday and every month renders 6 rows at most, padded with outside days (data-outside) so the panel height does not jump between months.
              • Today always carries the dot (data-today + aria-current="date"), even when selected or inside a range.
              • A range is start + end with aria-selected="true" and data-range="start|end"; the days between get data-range="middle". The footer input shows the range as "Sep 8 – Sep 14, 2026".
              • The footer is mandatory: the typed date in a small Input (with a leading calendar icon), Cancel (outline) and Apply (primary), size sm. Selection is committed by Apply, not by clicking a day.
              • Presets are for filters by period, not for forms; the list is fixed (Today, Yesterday, This week, Last week, This month, Last month) and the active one has aria-current="true".
              • Dates in the input and in triggers use the "Sep 11, 2026" format; never numeric-only dates that differ by locale.
              • The panel is statically positioned by the component; the app anchors it under its trigger with an 8px offset (Popover rules) and closes it on Escape and outside click.
              • Sizes: md by default; sm only inside dense toolbars. Do not scale the panel by other means.

              Accessibility

              • The panel is role="dialog" with an aria-label; focus moves into it on open (to the selected day or today) and returns to the trigger on close.
              • Days use a roving tabindex: one day is tabbable, Arrow keys move by day and week, Home/End jump to the row ends, PageUp/PageDown change the month.
              • Each day button needs a full accessible name ("Tuesday, September 8, 2026") via aria-label; the visible number alone is ambiguous.
              • The month name has aria-live="polite" so navigating months is announced; the nav buttons have aria-labels.
              • Disabled days stay in the DOM with disabled; explain the constraint near the trigger ("Deliveries start tomorrow").
              Forms

              FilterBar .cn-filter-baredit

              The toolbar above a table or list: a 320px search, dropdown filter buttons with chevrons, a date-range button, sort, a list/grid toggle, the result count, and a wrapping row of active-filter chips with an × each. Composes Input, Button and Tabs; adds only the chips and the view toggle.

              Use: Use above any collection view (customers, invoices, files) that can be searched, filtered or re-sorted. Not for form fields (Field), not for page-level tabs (Tabs on their own), and not inside a Card header (put a single search Input there).

              SimpleComponent previewSearch, three filter menus and the count in one run.
              HTML
              <div class="cn-filter-bar" data-variant="simple" data-align="start" role="search" aria-label="Filter customers" style="width:100%"><div class="cn-filter-bar__search"><div class="cn-input" data-variant="default" data-size="sm"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search customers…" aria-label="Search customers"></div></div><div class="cn-filter-bar__group"><button type="button" class="cn-button" aria-haspopup="menu" aria-expanded="false" data-variant="outline" data-size="sm"><span class="cn-button__label">Status</span><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></button><button type="button" class="cn-button" aria-haspopup="menu" aria-expanded="false" data-variant="outline" data-size="sm"><span class="cn-button__label">Plan</span><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></button><button type="button" class="cn-button" aria-haspopup="menu" aria-expanded="false" data-variant="outline" data-size="sm"><span class="cn-button__label">Owner</span><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></button></div><div class="cn-filter-bar__end"><span class="cn-filter-bar__count" aria-live="polite">128 results</span></div></div>
              Anatomy, props, states, rules

              Anatomy

              • .cn-filter-bar <div> — The bar: flex row that wraps, 12px gaps, role="search" or a labelled toolbar. Width comes from the layout.
              • .cn-filter-bar__search <div> · optional — Wrapper for a sm Input with a leading search icon; 320px wide, shrinks to 200px.
              • .cn-filter-bar__group <div> · optional — A cluster of outline sm Buttons with trailing chevrons that open filter menus (Status, Plan, Owner).
              • .cn-filter-bar__chips <div> · optional — The wrapping row of active filters. Takes the full width so it sits under the controls.
              • .cn-filter-bar__chip <span> · optional — One active filter: 28px pill with a hairline, "Key: value" and a 16px × remove button.
              • .cn-filter-bar__chip-key <span> · optional — The muted "Status:" prefix inside a chip.
              • .cn-filter-bar__chip-remove <button> · optional — The 16px × that removes this filter. aria-label "Remove filter Status: Active".
              • .cn-filter-bar__date <div> · optional — Wrapper for the date-range button: outline sm Button with a leading calendar icon and a trailing chevron that opens a DatePicker.
              • .cn-filter-bar__sort <div> · optional — Wrapper for the sort control: an outline sm Button ("Sort: Newest") that opens a Menu of radio items.
              • .cn-filter-bar__view <div> · optional — The list/grid toggle: a hairline box of 32px icon buttons, role="group".
              • .cn-filter-bar__view-item <button> · optional — One icon button inside the view toggle; aria-pressed="true" on the active layout, aria-label naming it.
              • .cn-filter-bar__count <span> · optional — The result count in body-sm muted ("128 results"), aria-live so filtering announces the new number.
              • .cn-filter-bar__clear <div> · optional — Wrapper for the link sm "Clear filters" Button. Only rendered when at least one filter is active.
              • .cn-filter-bar__tabs <div> · optional — Wrapper for a Tabs component (underline for segments of the collection, pill for quick date ranges).
              • .cn-filter-bar__end <div> · optional — The trailing cluster (count, sort, view, clear). With align="between" it is pushed to the right edge.

              Props

              propvalues (default outlined)meaning
              variantsimple with-chips with-date with-tabs densesimple = search + filter buttons + count (most tables). with-chips = the same plus a second line of active-filter chips with × and a Clear link (when filters persist). with-date = quick-range pill Tabs + a date-range button for time-based data (invoices, analytics). with-tabs = underline Tabs that segment the collection ("All / Active / Trial") with search on the right. dense = 8px gaps and 24px chips for toolbars inside drawers and cards.
              alignstart betweenstart = everything flows from the left in one run. between = the end cluster (count, sort, view) is pushed to the right edge, leaving the middle empty. Use between on full-width pages, start inside narrower panels.

              States

              stateselectormeaning
              chipRemoveHover .cn-filter-bar__chip-remove:hoverPointer over a chip's × (on the button; extraCss): muted fill, ink icon. (native :hover on .cn-filter-bar__chip-remove)
              chipRemoveFocus .cn-filter-bar__chip-remove:focus-visibleKeyboard focus on a chip's × shows the focus ring (extraCss). (native :focus-visible on .cn-filter-bar__chip-remove)
              viewPressed .cn-filter-bar__view-item[aria-pressed="true"]The active layout in the view toggle (on the item; extraCss): subtle fill, ink icon. (aria-pressed="true" on .cn-filter-bar__view-item)
              viewHover .cn-filter-bar__view-item:hoverPointer over a layout button: subtle fill (extraCss). (native :hover on .cn-filter-bar__view-item)
              viewFocus .cn-filter-bar__view-item:focus-visibleKeyboard focus on a layout button: an inset 2px ring (the toggle clips overflow) (extraCss). (native :focus-visible on .cn-filter-bar__view-item)

              Rules

              • Order from left to right: search, filter menus, date range, then the end cluster: count, sort, view toggle. Never put the count first.
              • Filter buttons are outline sm Buttons with a trailing chevron and open a Menu; the button label names the field ("Status"), never the value.
              • Chips show "Key: value" and only appear for active filters; the Clear link appears with the first chip and disappears with the last.
              • Search is 320px on pages, shrinks to 200px, and is the only Input in the bar. Put more fields in a filter Drawer.
              • The count uses the exact number and the noun ("128 results", "12 files") and updates live; no "Showing" prefix.
              • The view toggle has two or three layouts at most, icon-only, with one aria-pressed="true".
              • Sort is a button labelled "Sort: {option}" that opens a Menu of radio items; never a bare Select in the bar.
              • align="between" on full-width pages; align="start" inside panels narrower than 720px, where the bar wraps anyway.
              • dense only inside drawers, cards and split views; on a page the default heights keep 36px targets.

              Accessibility

              • Root is role="search" when it contains the search field, otherwise role="toolbar"; give it an aria-label naming the collection.
              • Filter buttons expose aria-haspopup="menu" and aria-expanded; the date button aria-haspopup="dialog".
              • Chip × buttons have aria-label "Remove filter {Key}: {value}"; removing one moves focus to the next chip or to the search field.
              • The view toggle is role="group" with aria-label; each item has aria-pressed and an aria-label naming the layout.
              • The count carries aria-live="polite" so screen readers hear the new number after filtering; keep the text short.
              Forms

              InputGroup .cn-input-groupedit

              An Input with addons attached to one or both ends: a muted text prefix or suffix ("https://", "USD"), a bare select, a leading icon or a trailing Button. One hairline box, one shadow, one focus ring around everything.

              Use: Use when a fixed part of the value belongs to the field: a protocol, a domain, a unit, a currency or a region selector, or when a "Copy" action must sit inside the field. For a lone prefix text the Input affix is enough; for a numeric stepper use NumberInput; for tokens use TagsInput. Pair with Field for the label.

              Leading text addon"https://" sits in the muted addon; the user types only the host.
              https://
              HTML
              <div class="cn-input-group" data-size="md" style="max-width:360px"><span class="cn-input-group__addon">https://</span><input class="cn-input-group__field" type="text" placeholder="lumen.co" aria-label="Website"></div>
              Trailing text addonThe domain is fixed, so it lives in the field instead of the helper text.
              @lumen.co
              HTML
              <div class="cn-input-group" data-size="md" style="max-width:360px"><input class="cn-input-group__field" type="text" value="maya.chen" aria-label="Workspace handle"><span class="cn-input-group__addon">@lumen.co</span></div>
              Leading select (currency)A bare native select as an addon; the chevron is an icon element placed right after it.
              HTML
              <div class="cn-input-group" data-size="md" style="max-width:360px"><select class="cn-input-group__select" aria-label="Currency"><option selected>USD</option><option>EUR</option><option>BRL</option><option>JPY</option></select><svg class="cn-input-group__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg><input class="cn-input-group__field" type="text" inputmode="decimal" value="1,200.00" aria-label="Amount"></div>
              Trailing button (copy)A ghost Button (size sm) that carries the __button part: no radius, stretched to the group, separated by a hairline.
              HTML
              <div class="cn-input-group" data-size="md" style="max-width:360px"><input class="cn-input-group__field" type="text" value="https://app.lumen.co/invite/8f3k2p" readonly aria-label="Invite link"><button type="button" class="cn-button cn-input-group__button" data-variant="ghost" data-size="sm"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-button__label">Copy</span></button></div>
              Icon and trailing dropdownLeading icon, then the field, then a select addon at the end.
              HTML
              <div class="cn-input-group" data-size="md" style="max-width:360px"><svg class="cn-input-group__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input-group__field" type="text" type="search" placeholder="Search prospects…" aria-label="Search prospects"><select class="cn-input-group__select" aria-label="Region"><option selected>All regions</option><option>Europe</option><option>Latin America</option><option>Asia-Pacific</option></select><svg class="cn-input-group__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div>
              Sizessm / md / lg follow the control heights; addons stretch with the group.
              https://
              https://
              https://
              HTML
              <div style="display:flex;flex-direction:column;gap:var(--cn-space-3);width:100%;min-width:0;max-width:360px"><div class="cn-input-group" data-size="sm" style="max-width:360px"><span class="cn-input-group__addon">https://</span><input class="cn-input-group__field" type="text" placeholder="lumen.co" aria-label="Website (small)"></div><div class="cn-input-group" data-size="md" style="max-width:360px"><span class="cn-input-group__addon">https://</span><input class="cn-input-group__field" type="text" placeholder="lumen.co" aria-label="Website (medium)"></div><div class="cn-input-group" data-size="lg" style="max-width:360px"><span class="cn-input-group__addon">https://</span><input class="cn-input-group__field" type="text" placeholder="lumen.co" aria-label="Website (large)"></div></div>
              InvalidPair with a Field error such as "Enter a full domain, like lumen.co".
              https://
              HTML
              <div class="cn-input-group" data-size="md" style="max-width:360px"><span class="cn-input-group__addon">https://</span><input class="cn-input-group__field" type="text" value="lumen" aria-invalid="true" aria-label="Website"></div>
              Disabled
              HTML
              <div class="cn-input-group" data-size="md" style="max-width:360px"><select class="cn-input-group__select" aria-label="Currency"><option selected>USD</option></select><svg class="cn-input-group__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg><input class="cn-input-group__field" type="text" value="1,200.00" disabled aria-label="Amount"></div>

              Matrix

              size
              sm
              https://
              md
              https://
              lg
              https://
              Anatomy, props, states, rules

              Anatomy

              • .cn-input-group <div> — The bordered wrapper (flex, stretch). Draws background, hairline, radius, shadow-xs and the focus ring on :focus-within. Carries data-size, data-invalid, data-disabled.
              • .cn-input-group__icon <svg> · optional — Decorative 20px icon: a leading icon before the field, or the chevron that follows a select addon. aria-hidden, pointer-events none.
              • .cn-input-group__addon <span> · optional — Static text addon such as "https://" or "USD": fg-muted, padding-inline space.3, separated from the field by a hairline. Leading when placed before the field, trailing when after.
              • .cn-input-group__select <select> · optional — A bare native <select> used as an addon (currency, region, protocol), followed by an icon element that draws the chevron. Leading or trailing.
              • .cn-input-group__field <input> — The native <input>: no border, no outline, fills the remaining width.
              • .cn-input-group__button <button> · optional — A trailing Button (class cn-button, variant ghost, size sm) that also carries this part so it loses its radius and stretches to the group height. One per group, always last.

              Props

              propvalues (default outlined)meaning
              sizesm md lgControl height and its matching padding, font and icon size.

              States

              stateselectormeaning
              hover:hover:not([data-disabled])Border strengthens on the whole group. (native :hover)
              focus:focus-withinBorder becomes the action color and the ring appears around the whole group, whichever child has focus. (native :focus-within on the wrapper)
              invalid[data-invalid], &:has([aria-invalid="true"])Danger border, danger ring on focus. Always accompanied by a Field error. (aria-invalid="true" on the input, or data-invalid on the wrapper)
              disabled[data-disabled], &:has(input:disabled)Grey fill, disabled text on every addon, not interactive. The trailing Button keeps full opacity but takes the disabled color. (disabled on the input (and on the button))

              Rules

              • One border for the whole group: the wrapper draws it; addons, the select and the button never draw their own outer border, only the hairline that separates them from the field.
              • Text addons hold what the user must not type: protocol, domain, unit, currency code. If the addon changes the meaning of the value (currency, region), make it a select addon instead.
              • At most one addon per side, and at most one Button per group, always trailing. A group with more than three children is a toolbar, not a field.
              • The trailing Button is a ghost cn-button, size sm, whatever the group size; it also carries the __button part so it stretches and loses its radius.
              • Addon labels are 1–2 words or a code ("USD", "@lumen.co", "https://"); never a full sentence.
              • Sizes: md in forms; sm inside toolbars and tables; lg on marketing and auth screens.
              • Width comes from the layout. The field takes the remaining width; addons keep their intrinsic width.
              • Error state must show a Field error under the group; color alone is not enough.

              Accessibility

              • The input has an accessible name (label for / aria-label). A text addon is visual: repeat its meaning in the label when it matters ("Website (https://)").
              • A select addon needs its own aria-label ("Currency"); the chevron svg is aria-hidden.
              • The trailing button is a real <button type="button"> with a verb label; it is a separate Tab stop after the input.
              • aria-invalid="true" and aria-describedby pointing at the Field error id when invalid.
              • Focus is shown on the whole group (:focus-within); the button shows an inset ring so it stays visible inside the clipped wrapper.
              Forms

              MultiSelect .cn-multi-selectedit

              A Combobox that keeps several values: chosen items sit as chips inside the field, and the listbox below shows a checkbox square on every option so the selection reads as a list of ticks. Typing filters; Enter toggles; Backspace removes the last chip.

              Use: Pick several items from a known list: assignees, tags from a fixed set, regions, products. For free-typed values use TagsInput; for one value use Select or Combobox; for 2–6 options that fit on screen use a Checkbox group. Pair with Field for the label.

              Closed with valuesChips inside the box, the input after them, the chevron at the end of the first line.
              Maya ChenDaniel Costa
                HTML
                <div class="cn-multi-select" data-size="md" data-state="closed" style="max-width:360px"><div class="cn-multi-select__chips"><span class="cn-multi-select__chip">Maya Chen<button type="button" class="cn-multi-select__chip-remove" aria-label="Remove Maya Chen"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-multi-select__chip">Daniel Costa<button type="button" class="cn-multi-select__chip-remove" aria-label="Remove Daniel Costa"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><input class="cn-multi-select__field" type="text" role="combobox" aria-autocomplete="list" aria-controls="ms-closed-list" aria-expanded="false" placeholder="Add people…" aria-label="Assignees"><svg class="cn-multi-select__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div><ul class="cn-multi-select__listbox" role="listbox" aria-multiselectable="true" id="ms-closed-list"></ul></div>
                Open with optionsMaya and Daniel are selected (filled squares), Sofia is highlighted by the keyboard, Aisha is disabled.
                Maya ChenDaniel Costa
                • Maya Chen
                • Daniel Costa
                • Sofia Almeida
                • Lucas Ferreira
                • Aisha Khan (on leave)
                HTML
                <div class="cn-multi-select" data-size="md" data-state="open" style="max-width:360px"><div class="cn-multi-select__chips"><span class="cn-multi-select__chip">Maya Chen<button type="button" class="cn-multi-select__chip-remove" aria-label="Remove Maya Chen"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-multi-select__chip">Daniel Costa<button type="button" class="cn-multi-select__chip-remove" aria-label="Remove Daniel Costa"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><input class="cn-multi-select__field" type="text" role="combobox" aria-autocomplete="list" aria-controls="ms-owner-list" aria-expanded="true" value="" aria-label="Assignees" aria-activedescendant="ms-owner-sofia"><svg class="cn-multi-select__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div><ul class="cn-multi-select__listbox" role="listbox" aria-multiselectable="true" id="ms-owner-list"><li class="cn-multi-select__option" role="option" id="ms-owner-maya" aria-selected="true"><span class="cn-multi-select__option-check" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span>Maya Chen</li><li class="cn-multi-select__option" role="option" id="ms-owner-daniel" aria-selected="true"><span class="cn-multi-select__option-check" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span>Daniel Costa</li><li class="cn-multi-select__option" role="option" id="ms-owner-sofia" aria-selected="false" data-highlighted><span class="cn-multi-select__option-check" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span>Sofia Almeida</li><li class="cn-multi-select__option" role="option" id="ms-owner-lucas" aria-selected="false"><span class="cn-multi-select__option-check" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span>Lucas Ferreira</li><li class="cn-multi-select__option" role="option" id="ms-owner-aisha" aria-selected="false" aria-disabled="true"><span class="cn-multi-select__option-check" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span>Aisha Khan (on leave)</li></ul></div>
                Open, no results
                Europe
                HTML
                <div class="cn-multi-select" data-size="md" data-state="open" style="max-width:360px"><div class="cn-multi-select__chips"><span class="cn-multi-select__chip">Europe<button type="button" class="cn-multi-select__chip-remove" aria-label="Remove Europe"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><input class="cn-multi-select__field" type="text" role="combobox" aria-autocomplete="list" aria-controls="ms-region-list" aria-expanded="true" value="Antarc" aria-label="Regions"><svg class="cn-multi-select__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div><ul class="cn-multi-select__listbox" role="listbox" aria-multiselectable="true" id="ms-region-list"><li class="cn-multi-select__empty" role="presentation">No regions match “Antarc”</li></ul></div>
                Empty (placeholder)Without chips it looks like a Combobox of the same size.
                  HTML
                  <div class="cn-multi-select" data-size="md" data-state="closed" style="max-width:360px"><div class="cn-multi-select__chips"><input class="cn-multi-select__field" type="text" role="combobox" aria-autocomplete="list" aria-controls="ms-empty-list" aria-expanded="false" placeholder="Select products…" aria-label="Products"><svg class="cn-multi-select__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div><ul class="cn-multi-select__listbox" role="listbox" aria-multiselectable="true" id="ms-empty-list"></ul></div>
                  Small and large
                  JapanNorway
                    JapanNorway
                      HTML
                      <div style="display:flex;flex-direction:column;gap:var(--cn-space-3);max-width:360px"><div class="cn-multi-select" data-size="sm" data-state="closed"><div class="cn-multi-select__chips"><span class="cn-multi-select__chip">Japan<button type="button" class="cn-multi-select__chip-remove" aria-label="Remove Japan"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-multi-select__chip">Norway<button type="button" class="cn-multi-select__chip-remove" aria-label="Remove Norway"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><input class="cn-multi-select__field" type="text" role="combobox" aria-autocomplete="list" aria-controls="ms-sm-list" aria-expanded="false" placeholder="Add a country…" aria-label="Countries (small)"><svg class="cn-multi-select__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div><ul class="cn-multi-select__listbox" role="listbox" aria-multiselectable="true" id="ms-sm-list"></ul></div><div class="cn-multi-select" data-size="lg" data-state="closed"><div class="cn-multi-select__chips"><span class="cn-multi-select__chip">Japan<button type="button" class="cn-multi-select__chip-remove" aria-label="Remove Japan"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-multi-select__chip">Norway<button type="button" class="cn-multi-select__chip-remove" aria-label="Remove Norway"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><input class="cn-multi-select__field" type="text" role="combobox" aria-autocomplete="list" aria-controls="ms-lg-list" aria-expanded="false" placeholder="Add a country…" aria-label="Countries (large)"><svg class="cn-multi-select__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div><ul class="cn-multi-select__listbox" role="listbox" aria-multiselectable="true" id="ms-lg-list"></ul></div></div>
                      InvalidPair with a Field error such as "Pick at least one reviewer".
                        HTML
                        <div class="cn-multi-select" data-size="md" data-state="closed" data-invalid style="max-width:360px"><div class="cn-multi-select__chips"><input class="cn-multi-select__field" type="text" role="combobox" aria-autocomplete="list" aria-controls="ms-invalid-list" aria-expanded="false" aria-invalid="true" placeholder="Add at least one reviewer…" aria-label="Reviewers"><svg class="cn-multi-select__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div><ul class="cn-multi-select__listbox" role="listbox" aria-multiselectable="true" id="ms-invalid-list"></ul></div>
                        Disabled
                        Elena RossiTomás Silva
                          HTML
                          <div class="cn-multi-select" data-size="md" data-state="closed" style="max-width:360px"><div class="cn-multi-select__chips"><span class="cn-multi-select__chip">Elena Rossi<button type="button" class="cn-multi-select__chip-remove" aria-label="Remove Elena Rossi" disabled><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-multi-select__chip">Tomás Silva<button type="button" class="cn-multi-select__chip-remove" aria-label="Remove Tomás Silva" disabled><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><input class="cn-multi-select__field" type="text" role="combobox" aria-autocomplete="list" aria-controls="ms-disabled-list" aria-expanded="false" disabled aria-label="Approvers"><svg class="cn-multi-select__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div><ul class="cn-multi-select__listbox" role="listbox" aria-multiselectable="true" id="ms-disabled-list"></ul></div>

                          Matrix

                          size
                          sm
                          Maya ChenDaniel Costa
                            md
                            Maya ChenDaniel Costa
                              lg
                              Maya ChenDaniel Costa
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-multi-select <div> — Wrapper (position: relative) holding the chips box and the listbox. Carries data-size, data-state="open|closed", data-invalid, data-disabled.
                                • .cn-multi-select__chips <div> — The bordered field box (flex, wrap, min-height = control height): draws background, hairline, radius, shadow-xs and the focus ring on :focus-within. Holds the chips, the input and the chevron.
                                • .cn-multi-select__chip <span> — One selected value: surface fill, hairline, radius md, 24px tall, body-sm, with its remove button.
                                • .cn-multi-select__chip-remove <button> — 16px × button at the end of a chip. aria-label "Remove {value}".
                                • .cn-multi-select__field <input> — The bare text input with role="combobox" (flex 1, min-width 80px). Filters the list; never holds a committed value.
                                • .cn-multi-select__icon <svg> — Chevron-down absolutely placed at the end of the first line; rotates when open. Decorative.
                                • .cn-multi-select__listbox <ul> — The panel under the box: surface-raised, hairline, radius panel, shadow-lg, padding space.1, 36px rows, scrolls after 256px. Hidden unless the root has data-state="open". role="listbox" aria-multiselectable="true".
                                • .cn-multi-select__option <li> — One row: checkbox square, then the label. role="option" with a unique id and aria-selected; data-highlighted for the keyboard cursor; aria-disabled="true" when not selectable.
                                • .cn-multi-select__option-check <span> — 16px checkbox square at the start of the row (hairline, radius sm) that fills with the action color and shows the check when the option is selected. Contains the check svg.
                                • .cn-multi-select__empty <li> · optional — Muted "No results" row rendered instead of options when the filter matches nothing.

                                Props

                                propvalues (default outlined)meaning
                                sizesm md lgControl height and its matching padding, font and icon size.

                                States

                                stateselectormeaning
                                hover:hover:not([data-disabled])Chips box border strengthens. (native :hover)
                                focus:focus-withinChips box border becomes the action color and the ring appears. Focus stays on the input while the list is open. (native :focus-within on the root)
                                open[data-state="open"]The listbox is visible under the box and the chevron points up. (data-state="open" on the root and aria-expanded="true" on the input)
                                invalid[data-invalid], &:has([aria-invalid="true"])Required selection missing or over the limit: danger border, danger ring on focus. Pair with a Field error. (aria-invalid="true" on the input, or data-invalid on the root)
                                disabled[data-disabled], &:has(input:disabled)Grey fill, disabled chips and chevron, not interactive. (disabled on the input (and on the chip remove buttons))
                                highlighted .cn-multi-select__option[data-highlighted]On the __option: the row under the keyboard cursor or pointer, bg-subtle. Exactly one at a time. Styled in extraCss. (data-highlighted on the option; mirror it with aria-activedescendant on the input)
                                selected .cn-multi-select__option[aria-selected="true"]On the __option: a chosen value; the checkbox square fills with the action color and shows the check, the label turns medium. Styled in extraCss. (aria-selected="true" on the option)
                                option-disabled .cn-multi-select__option[aria-disabled="true"]On the __option: not selectable, disabled ink, skipped by the keyboard. Styled in extraCss. (aria-disabled="true" on the option)

                                Rules

                                • Values come from the list only; typing filters, it never creates. If users must add their own values, use TagsInput.
                                • Selected items render as chips in the box in selection order and stay ticked in the list; removing a chip unticks the option and vice versa.
                                • The listbox renders in the flow directly under the box (margin-top space.1) inside the root; toggle data-state="open|closed" on the root. Move it to a popper layer only when it would be clipped.
                                • Options are 36px rows with a 16px checkbox square first; show at most 7 before scrolling (max-height 256px).
                                • Exactly one option is highlighted at a time (data-highlighted) and follows keyboard and pointer; Enter or Space toggles it without closing the list.
                                • When nothing matches, render the empty row ("No regions match “…”"); never an empty panel and never close the list.
                                • State the limit in the helper text when one exists ("Up to 5 reviewers") and mark the box invalid when it is exceeded, not silently ignore the click.
                                • Sizes: md in forms; sm in filter bars; lg on onboarding screens. Chips are 20 / 24 / 28px tall.

                                Accessibility

                                • The input has role="combobox", aria-expanded, aria-controls="<listbox id>", aria-autocomplete="list" and aria-activedescendant pointing at the highlighted option id.
                                • The list has role="listbox" aria-multiselectable="true"; each option has role="option", a unique id and aria-selected="true|false". Disabled options use aria-disabled="true".
                                • Keyboard: Down/Up move the highlight, Enter or Space toggle it, Escape closes, Backspace on an empty input removes the last chip. Focus stays on the input the whole time.
                                • Each chip remove button is a real <button> with aria-label "Remove {value}"; announce changes in a live region ("Daniel Costa removed, 1 selected").
                                • The checkbox square is decorative (aria-hidden); the state is carried by aria-selected on the option.
                                Forms

                                NumberInput .cn-number-inputedit

                                A numeric Input with an optional unit suffix and, in the stepper variant, two square ghost buttons (−, +) separated by hairlines at the end. The native spinner is hidden; the wrapper draws the border and focus ring.

                                Use: Quantities, seats, weights, percentages and other numbers the user adjusts by small steps. For free numeric text (an invoice total, a phone number) use Input with inputmode; for a currency amount with a selector use InputGroup; for a value on a range use Slider.

                                Stepper (default)Value on the left, −/+ at the end. Arrow keys step the value from the keyboard.
                                HTML
                                <div class="cn-number-input" data-variant="stepper" data-size="md" style="max-width:240px"><input class="cn-number-input__field" type="number" inputmode="decimal" value="12" aria-label="Seats" min="1" max="200" step="1"><button type="button" class="cn-number-input__decrement" aria-label="Decrease seats" tabindex="-1"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M3 8h10"/></svg></button><button type="button" class="cn-number-input__increment" aria-label="Increase seats" tabindex="-1"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg></button></div>
                                With unitThe unit is a muted suffix, not part of the value.
                                kg
                                HTML
                                <div class="cn-number-input" data-variant="stepper" data-size="md" style="max-width:240px"><input class="cn-number-input__field" type="number" inputmode="decimal" value="480" aria-label="Pallet weight" min="0" step="10"><span class="cn-number-input__unit">kg</span><button type="button" class="cn-number-input__decrement" aria-label="Decrease pallet weight" tabindex="-1"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M3 8h10"/></svg></button><button type="button" class="cn-number-input__increment" aria-label="Increase pallet weight" tabindex="-1"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg></button></div>
                                Plain (no stepper)For typed numbers where stepping makes no sense.
                                %
                                HTML
                                <div class="cn-number-input" data-variant="plain" data-size="md" style="max-width:240px"><input class="cn-number-input__field" type="number" inputmode="decimal" value="15" aria-label="Discount" min="0" max="100"><span class="cn-number-input__unit">%</span></div>
                                SizesStepper buttons are 32 / 36 / 40px wide and always stretch to the field height.
                                HTML
                                <div style="display:flex;flex-direction:column;gap:var(--cn-space-3);max-width:240px"><div class="cn-number-input" data-variant="stepper" data-size="sm" style="max-width:240px"><input class="cn-number-input__field" type="number" inputmode="decimal" value="3" aria-label="Quantity"><button type="button" class="cn-number-input__decrement" aria-label="Decrease quantity" tabindex="-1"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M3 8h10"/></svg></button><button type="button" class="cn-number-input__increment" aria-label="Increase quantity" tabindex="-1"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg></button></div><div class="cn-number-input" data-variant="stepper" data-size="md" style="max-width:240px"><input class="cn-number-input__field" type="number" inputmode="decimal" value="3" aria-label="Quantity"><button type="button" class="cn-number-input__decrement" aria-label="Decrease quantity" tabindex="-1"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M3 8h10"/></svg></button><button type="button" class="cn-number-input__increment" aria-label="Increase quantity" tabindex="-1"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg></button></div><div class="cn-number-input" data-variant="stepper" data-size="lg" style="max-width:240px"><input class="cn-number-input__field" type="number" inputmode="decimal" value="3" aria-label="Quantity"><button type="button" class="cn-number-input__decrement" aria-label="Decrease quantity" tabindex="-1"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M3 8h10"/></svg></button><button type="button" class="cn-number-input__increment" aria-label="Increase quantity" tabindex="-1"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg></button></div></div>
                                At the minimumThe decrement button is disabled at min; the increment at max.
                                HTML
                                <div class="cn-number-input" data-variant="stepper" data-size="md" style="max-width:240px"><input class="cn-number-input__field" type="number" inputmode="decimal" value="1" aria-label="Seats" min="1"><button type="button" class="cn-number-input__decrement" aria-label="Decrease seats" tabindex="-1" disabled><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M3 8h10"/></svg></button><button type="button" class="cn-number-input__increment" aria-label="Increase seats" tabindex="-1"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg></button></div>
                                InvalidPair with a Field error such as "Enter at least 1 seat".
                                HTML
                                <div class="cn-number-input" data-variant="stepper" data-size="md" style="max-width:240px"><input class="cn-number-input__field" type="number" inputmode="decimal" value="0" aria-label="Seats" min="1" aria-invalid="true"><button type="button" class="cn-number-input__decrement" aria-label="Decrease seats" tabindex="-1"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M3 8h10"/></svg></button><button type="button" class="cn-number-input__increment" aria-label="Increase seats" tabindex="-1"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg></button></div>
                                Disabled
                                seats
                                HTML
                                <div class="cn-number-input" data-variant="stepper" data-size="md" style="max-width:240px"><input class="cn-number-input__field" type="number" inputmode="decimal" value="40" aria-label="Seats" disabled><span class="cn-number-input__unit">seats</span><button type="button" class="cn-number-input__decrement" aria-label="Decrease seats" tabindex="-1" disabled><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M3 8h10"/></svg></button><button type="button" class="cn-number-input__increment" aria-label="Increase seats" tabindex="-1" disabled><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg></button></div>

                                Matrix

                                variant ↓ / size →smmdlg
                                stepper
                                plain
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-number-input <div> — The bordered wrapper (inline-flex, stretch): background, hairline, radius, shadow-xs, focus ring on :focus-within. Carries data-variant, data-size, data-invalid, data-disabled.
                                • .cn-number-input__field <input> — The native <input type="number" inputmode="decimal">. No border, tabular numerals, spinner hidden.
                                • .cn-number-input__unit <span> · optional — Muted suffix after the value ("kg", "%", "seats"). Not part of the value.
                                • .cn-number-input__decrement <button> · optional — Square ghost button with a minus icon, separated by a hairline. tabindex="-1" (keyboard users use the arrow keys). Disable it at min.
                                • .cn-number-input__increment <button> · optional — Square ghost button with a plus icon, last child. Disable it at max.

                                Props

                                propvalues (default outlined)meaning
                                sizesm md lgControl height and its matching padding, font and icon size.
                                variantstepper plainstepper = value plus the −/+ buttons (quantities, seats, anything adjusted in small steps); plain = value and unit only, no buttons (percentages, weights and other typed numbers where stepping makes no sense).

                                States

                                stateselectormeaning
                                hover:hover:not([data-disabled])Border strengthens. (native :hover)
                                focus:focus-withinBorder becomes the action color and the ring appears (also when a stepper button is focused). (native :focus-within on the wrapper)
                                invalid[data-invalid], &:has([aria-invalid="true"])Out of range or not a number: danger border, danger ring on focus. Pair with a Field error. (aria-invalid="true" on the input, or data-invalid on the wrapper)
                                disabled[data-disabled], &:has(input:disabled)Grey fill, disabled text and buttons, not interactive. (disabled on the input (and on both buttons))
                                stepHover .cn-number-input__decrement:hover:not(:disabled)On a stepper button: subtle fill and default ink. Same for __increment. Styled in extraCss. (native :hover on a stepper button)
                                stepDisabled .cn-number-input__decrement:disabledOn a stepper button at the min (or __increment at the max): disabled ink, no hover. Styled in extraCss. (disabled on the button)

                                Rules

                                • Use type="number" with inputmode="decimal" and real min / max / step attributes; the stepper buttons call stepDown() / stepUp() on the input, never their own arithmetic.
                                • Disable the decrement at min and the increment at max instead of clamping silently.
                                • The unit is a suffix after the value ("kg", "%", "seats"); a currency with a selector is an InputGroup, not a NumberInput.
                                • stepper for small-step adjustments (quantities, seats, days); plain for typed values (percentages, weights, prices). Do not add steppers to a value nobody increments by one.
                                • Numbers are right-aligned only in tables; inside forms they stay left-aligned like every other field.
                                • Width comes from the layout; cap it with max-width on the Field (120–240px) since numbers are short.
                                • Sizes: md in forms; sm in tables and toolbars; lg on marketing screens.
                                • Invalid must be accompanied by a Field error that states the allowed range ("Between 1 and 200").

                                Accessibility

                                • The input has an accessible name (label for / aria-label); the stepper buttons say "Increase {name}" / "Decrease {name}".
                                • Stepper buttons use tabindex="-1": keyboard users step with Arrow Up/Down (native) and never need to tab through the buttons.
                                • Keep the native input so aria-valuemin / max come for free; when invalid set aria-invalid="true" and aria-describedby to the error id.
                                • Focus is shown on the wrapper (:focus-within); a clicked stepper shows an inset ring so it stays visible inside the clipped wrapper.
                                Forms

                                PinInput .cn-pin-inputedit

                                A row of square one-character cells for a verification code. Each cell is a native input with a "0" placeholder; the typed digit renders large, centered, in the action color. Focus moves cell to cell.

                                Use: Use for one-time passcodes, 2FA codes and short confirmation codes (4 or 6 digits) sent by email or SMS. Not for phone numbers, card numbers or anything the user copies from a document: use Input with inputmode there.

                                Six digits, half enteredTwo groups of three with a separator. Filled cells carry data-filled; the empty ones show the "0" placeholder.
                                HTML
                                <div class="cn-pin-input" data-size="md" data-length="6" role="group" aria-label="Verification code"><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" autocomplete="one-time-code" aria-label="Digit 1 of 6" value="4" data-filled><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 2 of 6" value="8" data-filled><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 3 of 6" value="2" data-filled><span class="cn-pin-input__separator" aria-hidden="true">-</span><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 4 of 6"><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 5 of 6"><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 6 of 6"></div>
                                Four digitsA single group with a wider gap and no separator.
                                HTML
                                <div class="cn-pin-input" data-size="md" data-length="4" role="group" aria-label="Verification code"><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" autocomplete="one-time-code" aria-label="Digit 1 of 4"><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 2 of 4"><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 3 of 4"><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 4 of 4"></div>
                                CompleteAll six cells filled: the form can submit automatically.
                                HTML
                                <div class="cn-pin-input" data-size="md" data-length="6" role="group" aria-label="Verification code"><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" autocomplete="one-time-code" aria-label="Digit 1 of 6" value="7" data-filled><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 2 of 6" value="3" data-filled><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 3 of 6" value="1" data-filled><span class="cn-pin-input__separator" aria-hidden="true">-</span><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 4 of 6" value="9" data-filled><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 5 of 6" value="0" data-filled><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 6 of 6" value="5" data-filled></div>
                                Sizes48 / 64 / 80px cells with heading-xl / display-md / display-lg digits.
                                HTML
                                <div style="display:flex;flex-direction:column;gap:var(--cn-space-4);align-items:flex-start"><div class="cn-pin-input" data-size="sm" data-length="4" role="group" aria-label="Verification code"><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" autocomplete="one-time-code" aria-label="Digit 1 of 4" value="2" data-filled><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 2 of 4" value="6" data-filled><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 3 of 4"><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 4 of 4"></div><div class="cn-pin-input" data-size="md" data-length="4" role="group" aria-label="Verification code"><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" autocomplete="one-time-code" aria-label="Digit 1 of 4" value="2" data-filled><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 2 of 4" value="6" data-filled><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 3 of 4"><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 4 of 4"></div><div class="cn-pin-input" data-size="lg" data-length="4" role="group" aria-label="Verification code"><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" autocomplete="one-time-code" aria-label="Digit 1 of 4" value="2" data-filled><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 2 of 4" value="6" data-filled><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 3 of 4"><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 4 of 4"></div></div>
                                InvalidEvery cell takes the danger border; the message under the row says what to do next.

                                That code has expired. We sent a new one to maya.chen@lumen.co.

                                HTML
                                <div style="display:flex;flex-direction:column;gap:var(--cn-space-2);align-items:flex-start"><div class="cn-pin-input" data-size="md" data-length="6" data-invalid role="group" aria-label="Verification code"><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" autocomplete="one-time-code" aria-label="Digit 1 of 6" value="4" data-filled aria-invalid="true" aria-describedby="pin-error"><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 2 of 6" value="8" data-filled aria-invalid="true" aria-describedby="pin-error"><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 3 of 6" value="2" data-filled aria-invalid="true" aria-describedby="pin-error"><span class="cn-pin-input__separator" aria-hidden="true">-</span><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 4 of 6" value="1" data-filled aria-invalid="true" aria-describedby="pin-error"><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 5 of 6" value="1" data-filled aria-invalid="true" aria-describedby="pin-error"><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 6 of 6" value="9" data-filled aria-invalid="true" aria-describedby="pin-error"></div><p class="cn-text-body-sm" id="pin-error" style="color:var(--cn-color-fg-danger);margin:0">That code has expired. We sent a new one to maya.chen@lumen.co.</p></div>
                                Disabled (verifying)Locked while the code is checked; the digits stay visible.
                                HTML
                                <div class="cn-pin-input" data-size="md" data-length="6" role="group" aria-label="Verification code"><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" autocomplete="one-time-code" aria-label="Digit 1 of 6" value="7" data-filled disabled><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 2 of 6" value="3" data-filled disabled><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 3 of 6" value="1" data-filled disabled><span class="cn-pin-input__separator" aria-hidden="true">-</span><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 4 of 6" value="9" data-filled disabled><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 5 of 6" value="0" data-filled disabled><input class="cn-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="0" aria-label="Digit 6 of 6" value="5" data-filled disabled></div>

                                Matrix

                                size ↓ / length →46
                                sm
                                md
                                lg
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-pin-input <div> — The row (flex, gap space.2). role="group" with an aria-label; carries data-size, data-length, data-invalid, data-disabled.
                                • .cn-pin-input__cell <input> — One <input maxlength="1" inputmode="numeric">: square, centered display text in fg-action, hairline, radius lg, shadow-xs. Set data-filled once it holds a character.
                                • .cn-pin-input__separator <span> · optional — A muted "-" between the two halves of a 6-digit code. aria-hidden.

                                Props

                                propvalues (default outlined)meaning
                                sizesm md lgControl height and its matching padding, font and icon size.
                                length4 64 = short codes (app confirmations, kiosk PINs), rendered as one group with a wider gap; 6 = the common SMS / authenticator code, rendered as two groups of three with a separator between them.

                                States

                                stateselectormeaning
                                focus .cn-pin-input__cell:focusOn the __cell (not the root): the focused cell shows the action border and the ring. Styled in extraCss because it lives on a child. (native :focus on a cell)
                                filled .cn-pin-input__cell[data-filled]On the __cell: the cell holds a character; the border strengthens so the progress is visible at a glance. Styled in extraCss. (data-filled on the cell (set it whenever value is not empty))
                                invalid[data-invalid], &:has([aria-invalid="true"])Wrong or expired code: every cell takes the danger border, the focused cell the danger ring. Pair with a message under the row. (aria-invalid="true" on the cells, or data-invalid on the root)
                                disabled[data-disabled], &:has(input:disabled)While the code is being verified or after too many attempts: grey cells, disabled text, cursor not-allowed. (disabled on the cells)

                                Rules

                                • One character per cell, numeric keyboard on mobile (inputmode="numeric", pattern="[0-9]*"), and autocomplete="one-time-code" on the first cell so the OS can offer the SMS code.
                                • Typing a digit moves focus to the next cell; Backspace on an empty cell moves back and clears the previous one; pasting a full code fills every cell at once.
                                • Submit automatically when the last cell is filled, and show the disabled state while verifying; never make the user press a button after typing six digits.
                                • Six digits are displayed as 3 + 3 with a separator; four digits as one group. Do not use other lengths or mix sizes in one form.
                                • Mark filled cells with data-filled so the border tells the progress without reading the digits.
                                • On error, keep the digits, set the invalid state and put a plain sentence under the row ("That code has expired. We sent a new one."). Clear the error on the next keystroke.
                                • Sizes: md on auth screens; sm inside dialogs and settings panels; lg only on a dedicated verification page.
                                • The digit is painted in the action color; the placeholder "0" in the placeholder color. Never dim the typed digit.

                                Accessibility

                                • The root is role="group" with aria-label ("Verification code"); each cell has aria-label "Digit n of 6" so the position is announced.
                                • Cells are native inputs: focus, disabled and invalid come from the platform. Do not rebuild them with contenteditable divs.
                                • When invalid, set aria-invalid="true" on the cells and aria-describedby on the group (or the cells) pointing at the error message id.
                                • The separator is aria-hidden; it has no meaning beyond grouping.
                                • Focus ring appears on the focused cell only; moving focus programmatically must keep the ring visible.
                                Forms

                                RichTextEditor .cn-rich-text-editoredit

                                A formatted-text field: the Input frame (control border, radius card, shadow-xs, focus ring on :focus-within) with a wrapping toolbar of 32px ghost tool buttons separated into groups by hairlines, a block-type Select, a contenteditable area with a placeholder and prose styles, and an optional footer with a character count and actions.

                                Use: Use where people write more than a note: descriptions, announcements, email bodies, comments with formatting. Keep the toolbar to what the destination can render. For plain multi-line text use Textarea; for code use Code; for read-only formatted text use Prose.

                                Default with contentComponent previewBlock-type select, inline styles, headings, lists, link and image. The bulleted-list tool is pressed because the caret sits in the list.

                                Release notes — September

                                This month we shipped the new reporting workspace and a faster search index. Highlights:

                                • Custom dashboards for every workspace
                                • Search results in under 200 ms
                                • Exports to CSV and Google Sheets

                                Read the full changelog or reply to this note with questions.

                                HTML
                                <div class="cn-rich-text-editor" data-variant="default" data-size="md" style="width:100%;max-width:640px"><div class="cn-rich-text-editor__toolbar" role="toolbar" aria-label="Formatting"><div class="cn-rich-text-editor__select"><div class="cn-select" data-variant="filled" data-size="sm"><select class="cn-select__field" aria-label="Block type"><option value="p" selected>Paragraph</option><option value="h1">Heading 1</option><option value="h2">Heading 2</option><option value="h3">Heading 3</option><option value="quote">Quote</option></select><svg class="cn-select__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div></div><div class="cn-rich-text-editor__tool-group" role="group"><button type="button" class="cn-rich-text-editor__tool" aria-label="Bold" aria-pressed="false"><svg class="cn-icon" viewBox="0 0 16 16" aria-hidden="true"><text x="8" y="12" text-anchor="middle" font-size="12" fill="currentColor" font-family="inherit" font-weight="700">B</text></svg></button><button type="button" class="cn-rich-text-editor__tool" aria-label="Italic" aria-pressed="false"><svg class="cn-icon" viewBox="0 0 16 16" aria-hidden="true"><text x="8" y="12" text-anchor="middle" font-size="12" fill="currentColor" font-family="inherit" font-style="italic" font-weight="500">I</text></svg></button><button type="button" class="cn-rich-text-editor__tool" aria-label="Underline" aria-pressed="false"><svg class="cn-icon" viewBox="0 0 16 16" aria-hidden="true"><text x="8" y="12" text-anchor="middle" font-size="12" fill="currentColor" font-family="inherit" text-decoration="underline" font-weight="500">U</text></svg></button></div><div class="cn-rich-text-editor__tool-group" role="group"><button type="button" class="cn-rich-text-editor__tool" aria-label="Heading 1" aria-pressed="false"><svg class="cn-icon" viewBox="0 0 16 16" aria-hidden="true"><text x="8" y="12" text-anchor="middle" font-size="12" fill="currentColor" font-family="inherit" font-size="9" font-weight="700">H1</text></svg></button><button type="button" class="cn-rich-text-editor__tool" aria-label="Heading 2" aria-pressed="false"><svg class="cn-icon" viewBox="0 0 16 16" aria-hidden="true"><text x="8" y="12" text-anchor="middle" font-size="12" fill="currentColor" font-family="inherit" font-size="9" font-weight="700">H2</text></svg></button></div><div class="cn-rich-text-editor__tool-group" role="group"><button type="button" class="cn-rich-text-editor__tool" aria-label="Bulleted list" aria-pressed="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M6 4h7M6 8h7M6 12h7"/><circle cx="3" cy="4" r=".9" fill="currentColor" stroke="none"/><circle cx="3" cy="8" r=".9" fill="currentColor" stroke="none"/><circle cx="3" cy="12" r=".9" fill="currentColor" stroke="none"/></svg></button><button type="button" class="cn-rich-text-editor__tool" aria-label="Numbered list" aria-pressed="false"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6.5 4h7M6.5 8h7M6.5 12h7M2.25 3l1-.5v3M2 9.25a1 1 0 011.75.5c0 .8-1.75 1.25-1.75 2.25h2"/></svg></button></div><div class="cn-rich-text-editor__tool-group" role="group"><button type="button" class="cn-rich-text-editor__tool" aria-label="Insert link"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6.5 9.5l3-3M7 11.25l-1.25 1.25a2.12 2.12 0 01-3-3L4 8.25M9 4.75l1.25-1.25a2.12 2.12 0 013 3L12 7.75"/></svg></button><button type="button" class="cn-rich-text-editor__tool" aria-label="Insert image"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="9" rx="1.5"/><circle cx="6" cy="7" r="1"/><path d="M13.5 10.5l-3-3-4 4"/></svg></button></div></div><div class="cn-rich-text-editor__area" contenteditable="true" role="textbox" aria-multiline="true" aria-label="Note" data-placeholder="Write something…"><h3>Release notes — September</h3><p>This month we shipped the new reporting workspace and a faster search index. Highlights:</p><ul><li>Custom dashboards for every workspace</li><li>Search results in under 200 ms</li><li>Exports to CSV and Google Sheets</li></ul><p>Read the <a href="#">full changelog</a> or reply to this note with questions.</p></div></div>
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-rich-text-editor <div> — The frame: bg-surface, control border, radius card, shadow-xs, flex column, overflow hidden. Focus ring on :focus-within; data-invalid and data-disabled for those states.
                                • .cn-rich-text-editor__toolbar <div> — role="toolbar": wrapping row of tool groups and the block-type select, 8px padding, bottom hairline.
                                • .cn-rich-text-editor__tool-group <div> — A cluster of related tools (inline styles, headings, lists, inserts); every group after the first gets a hairline on its left.
                                • .cn-rich-text-editor__tool <button> — 32px ghost icon button (28px in sm). Toggles carry aria-pressed; actions (link, image) do not.
                                • .cn-rich-text-editor__select <div> · optional — Wrapper for a filled sm Select of block types (Paragraph, Heading 1–3, Quote); 160px wide.
                                • .cn-rich-text-editor__area <div> — The contenteditable (role="textbox", aria-multiline): min-height 160px, 16px padding, body-md, prose styles for headings, lists, links and quotes. Shows data-placeholder while empty.
                                • .cn-rich-text-editor__footer <div> · optional — Optional bottom row with a top hairline: character count on the left, actions on the right.
                                • .cn-rich-text-editor__count <span> · optional — Character count in body-xs tabular ("412 / 2,000"); fg-danger when over the limit.
                                • .cn-rich-text-editor__actions <div> · optional — Right-aligned row of sm Buttons (ghost Cancel, primary Save).

                                Props

                                propvalues (default outlined)meaning
                                variantdefault minimal with-footerdefault = full toolbar (block type, inline styles, headings, lists, link, image) above a 160px area. minimal = inline styles and link only, no hairline under the toolbar, a 96px area: comments and short descriptions. with-footer = default plus a footer with a character count and Cancel/Save actions, for notes that are saved explicitly.
                                sizesm mdmd = 32px tools, 16px area padding, body-md text (forms and full pages). sm = 28px tools, 12px padding, body-sm text for side panels and comment boxes.

                                States

                                stateselectormeaning
                                focus:focus-withinWriting or using the toolbar: the border becomes action-colored and the 3px ring appears on the frame. (native :focus-within on the root)
                                invalid[data-invalid]Validation failed (required, over the limit): danger border, danger ring while focused. Pair with a Field error. (data-invalid on the root plus aria-invalid="true" on the area)
                                disabled[data-disabled]Read-only or locked: grey fill, disabled text, tools at 50% and inert, area not editable. (data-disabled on the root plus contenteditable="false" on the area)
                                toolHover .cn-rich-text-editor__tool:hoverPointer over a tool (extraCss): subtle fill, ink icon. (native :hover on .cn-rich-text-editor__tool)
                                toolPressed .cn-rich-text-editor__tool[aria-pressed="true"]A formatting toggle that applies to the selection (extraCss): subtle fill and ink icon at rest. (aria-pressed="true" on .cn-rich-text-editor__tool)
                                toolFocus .cn-rich-text-editor__tool:focus-visibleKeyboard focus on a tool shows the focus ring (extraCss). (native :focus-visible on .cn-rich-text-editor__tool)
                                toolDisabled .cn-rich-text-editor__tool:disabledA tool that does not apply now (extraCss): 50% opacity, not-allowed. (disabled on .cn-rich-text-editor__tool)

                                Rules

                                • Offer only what the destination renders: an email body gets inline styles and links; a document gets headings and lists too. Never a toolbar with twenty tools.
                                • Group tools by kind (block type, inline, headings, lists, inserts) with the hairline between groups; keep the same order in every editor of the product.
                                • Toggles (bold, italic, lists) show aria-pressed for the current selection; actions (link, image) open a small Dialog or Popover, never a browser prompt.
                                • The area starts at 160px (96px minimal) and grows with content; never an inner scrollbar unless the editor sits in a fixed-height panel.
                                • Placeholders are an example of the expected content ("Write the release notes…"), not a label; the label lives in the Field.
                                • Show a character count only with a hard limit; over the limit the count turns danger and the frame becomes invalid, but typing is not blocked.
                                • Paste keeps only the formatting the toolbar offers; strip colors, fonts and sizes.
                                • Save explicitly with a footer (with-footer) when the text is a record; autosave with a status line when it is a draft. Never both.
                                • sm inside comment boxes and side panels; md in forms and full pages.

                                Accessibility

                                • The area is role="textbox" with aria-multiline="true" and an accessible name (a Field label or aria-label); set aria-invalid and aria-describedby with the error when invalid.
                                • The toolbar is role="toolbar" with an aria-label; Tab enters it once, Left/Right move between tools, and the tools are real <button>s with aria-label.
                                • Formatting toggles expose aria-pressed; the block-type Select is a native <select> so it works everywhere.
                                • Keyboard shortcuts (Cmd/Ctrl+B, I, U, K) mirror the toolbar and are listed in a Tooltip on each tool.
                                • Announce the character count with aria-live="polite" and say "over the limit" in the Field error, not only in color.
                                Forms

                                TagsInput .cn-tags-inputedit

                                An Input that holds many values as chips: emails, keywords, labels. One hairline box with the control height as its minimum; chips wrap, and a bare text field grows into the remaining space of the last line.

                                Use: Free-form lists typed by the user: invite emails, keywords, tags, domains. When the values must come from a known list use MultiSelect; for a single value use Input; for filters you toggle use Tag. Pair with Field for the label and helper ("Press Enter to add").

                                Invite by emailChips wrap inside the box; the field grows into the rest of the last line.
                                maya.chen@lumen.codaniel.costa@lumen.cosofia.almeida@lumen.co
                                HTML
                                <div class="cn-tags-input" data-size="md" style="max-width:420px"><span class="cn-tags-input__tag">maya.chen@lumen.co<button type="button" class="cn-tags-input__tag-remove" aria-label="Remove maya.chen@lumen.co"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-tags-input__tag">daniel.costa@lumen.co<button type="button" class="cn-tags-input__tag-remove" aria-label="Remove daniel.costa@lumen.co"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-tags-input__tag">sofia.almeida@lumen.co<button type="button" class="cn-tags-input__tag-remove" aria-label="Remove sofia.almeida@lumen.co"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><input class="cn-tags-input__field" type="text" placeholder="Add teammate…" aria-label="Invite by email" inputmode="email"></div>
                                Keywords
                                Frozen seafoodImporterJapan
                                HTML
                                <div class="cn-tags-input" data-size="md" style="max-width:420px"><span class="cn-tags-input__tag">Frozen seafood<button type="button" class="cn-tags-input__tag-remove" aria-label="Remove Frozen seafood"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-tags-input__tag">Importer<button type="button" class="cn-tags-input__tag-remove" aria-label="Remove Importer"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-tags-input__tag">Japan<button type="button" class="cn-tags-input__tag-remove" aria-label="Remove Japan"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><input class="cn-tags-input__field" type="text" placeholder="Add a keyword and press Enter" aria-label="Keywords"></div>
                                EmptyWith no chips it looks exactly like an Input of the same size.
                                HTML
                                <div class="cn-tags-input" data-size="md" style="max-width:420px"><input class="cn-tags-input__field" type="text" placeholder="Add domains, comma separated" aria-label="Allowed domains"></div>
                                SizesChips are 20 / 24 / 28px tall and the box keeps the control height as its minimum.
                                OsakaTokyo
                                OsakaTokyo
                                OsakaTokyo
                                HTML
                                <div style="display:flex;flex-direction:column;gap:var(--cn-space-3);max-width:420px"><div class="cn-tags-input" data-size="sm" style="max-width:420px"><span class="cn-tags-input__tag">Osaka<button type="button" class="cn-tags-input__tag-remove" aria-label="Remove Osaka"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-tags-input__tag">Tokyo<button type="button" class="cn-tags-input__tag-remove" aria-label="Remove Tokyo"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><input class="cn-tags-input__field" type="text" placeholder="Add a city…" aria-label="Cities (small)"></div><div class="cn-tags-input" data-size="md" style="max-width:420px"><span class="cn-tags-input__tag">Osaka<button type="button" class="cn-tags-input__tag-remove" aria-label="Remove Osaka"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-tags-input__tag">Tokyo<button type="button" class="cn-tags-input__tag-remove" aria-label="Remove Tokyo"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><input class="cn-tags-input__field" type="text" placeholder="Add a city…" aria-label="Cities (medium)"></div><div class="cn-tags-input" data-size="lg" style="max-width:420px"><span class="cn-tags-input__tag">Osaka<button type="button" class="cn-tags-input__tag-remove" aria-label="Remove Osaka"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-tags-input__tag">Tokyo<button type="button" class="cn-tags-input__tag-remove" aria-label="Remove Tokyo"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><input class="cn-tags-input__field" type="text" placeholder="Add a city…" aria-label="Cities (large)"></div></div>
                                InvalidThe box takes the danger border; the message names the bad value.
                                aisha.khan@lumen.co

                                "noah.berg@lumen" is not a valid email address.

                                HTML
                                <div class="cn-tags-input" data-size="md" data-invalid style="max-width:420px"><span class="cn-tags-input__tag">aisha.khan@lumen.co<button type="button" class="cn-tags-input__tag-remove" aria-label="Remove aisha.khan@lumen.co"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><input class="cn-tags-input__field" type="text" value="noah.berg@lumen" aria-invalid="true" aria-label="Invite by email"></div><p class="cn-text-body-sm" style="color:var(--cn-color-fg-danger);margin:var(--cn-space-1-5) 0 0">"noah.berg@lumen" is not a valid email address.</p>
                                Disabled
                                lumen.columen.dev
                                HTML
                                <div class="cn-tags-input" data-size="md" style="max-width:420px"><span class="cn-tags-input__tag">lumen.co<button type="button" class="cn-tags-input__tag-remove" aria-label="Remove lumen.co" disabled><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-tags-input__tag">lumen.dev<button type="button" class="cn-tags-input__tag-remove" aria-label="Remove lumen.dev" disabled><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><input class="cn-tags-input__field" type="text" disabled aria-label="Allowed domains"></div>

                                Matrix

                                size
                                sm
                                maya.chen@lumen.codaniel.costa@lumen.cosofia.almeida@lumen.co
                                md
                                maya.chen@lumen.codaniel.costa@lumen.cosofia.almeida@lumen.co
                                lg
                                maya.chen@lumen.codaniel.costa@lumen.cosofia.almeida@lumen.co
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-tags-input <div> — The bordered box (flex, wrap, align center, gap space.1, padding space.1.5, min-height = control height). Draws background, hairline, radius, shadow-xs and the focus ring on :focus-within. Clicking anywhere focuses the field.
                                • .cn-tags-input__tag <span> — One value chip: surface fill, hairline, radius md, 24px tall, body-sm text, followed by its remove button.
                                • .cn-tags-input__tag-remove <button> — 16px button with an × icon at the end of a chip. aria-label "Remove {value}".
                                • .cn-tags-input__field <input> — The bare text input (flex 1, min-width 80px): no border, no outline. Enter or comma commits the text as a chip.

                                Props

                                propvalues (default outlined)meaning
                                sizesm md lgControl height and its matching padding, font and icon size.

                                States

                                stateselectormeaning
                                hover:hover:not([data-disabled])Border strengthens. (native :hover)
                                focus:focus-withinBorder becomes the action color and the ring appears around the whole box. (native :focus-within on the box)
                                invalid[data-invalid], &:has([aria-invalid="true"])A value failed validation (bad email, duplicate, too many): danger border, danger ring on focus. Pair with a Field error. (aria-invalid="true" on the input, or data-invalid on the box)
                                disabled[data-disabled], &:has(input:disabled)Grey fill, chips and text in the disabled color, remove buttons inert. (disabled on the input (and on the remove buttons))
                                removeHover .cn-tags-input__tag-remove:hoverOn the __tag-remove button: subtle fill and default ink. Styled in extraCss because it lives on a child. (native :hover on the remove button)
                                removeFocus .cn-tags-input__tag-remove:focus-visibleOn the __tag-remove button: its own focus ring. Styled in extraCss. (native :focus-visible on the remove button)

                                Rules

                                • Enter, comma and blur commit the typed text as a chip; Backspace on an empty field removes the last chip; pasting "a, b, c" creates three chips.
                                • Validate at commit time, not per keystroke: a bad value stays as text in the field with the invalid state and a message that quotes it.
                                • Chips show the value as typed (an email, a keyword), 1–4 words, never truncated; long values wrap to a new line instead.
                                • Clicking anywhere in the box focuses the field; the box, not the field, shows the focus ring.
                                • Set a maximum where one exists (seats, recipients) and say it in the helper text ("Up to 20 emails"); disable the field, not the remove buttons, when it is reached.
                                • Sizes: md in forms; sm in filter bars and dense panels; lg on onboarding screens.
                                • Never mix a TagsInput with a dropdown of suggestions in the same box; that is a MultiSelect.
                                • Width comes from the layout; never set a fixed width, and let the box grow in height as chips wrap.

                                Accessibility

                                • The field has an accessible name (label for / aria-label); announce additions and removals in a visually hidden live region ("Added maya.chen@lumen.co, 4 recipients").
                                • Each remove button is a real <button> with aria-label "Remove {value}"; after removing, focus returns to the field.
                                • Chips themselves are not focusable; the keyboard interacts through the field (Backspace) and the remove buttons (Tab).
                                • When invalid, aria-invalid="true" on the field and aria-describedby pointing at the message id.
                                Navigation

                                Topbar .cn-topbaredit

                                The application header of the reference: a 64px bar on a hairline with the logo, a row of 32px navigation pills, a search field and the account on the right; optionally a second 64px tier with more pills or underline tabs. Sticky, opaque or translucent.

                                Use: One per app, at the top of every authenticated page when the app has 3–6 top-level destinations and no Sidebar. Holds the product mark, the primary navigation, global search and the account. Page titles, breadcrumbs and filters do not live here; they belong to the PageHeader below.

                                Simple headerComponent preview64px bar, 1280px container with 32px padding. Dashboard is current (gray-50 pill); notifications carry the 14px red count.
                                1280 × 384
                                HTML
                                <header class="cn-topbar" data-variant="solid" data-align="start"><div class="cn-topbar__inner"><a href="#" class="cn-topbar__brand"><span class="cn-topbar__logo" aria-hidden="true"></span>Lumen</a><nav class="cn-topbar__nav" aria-label="Primary"><a href="#" class="cn-topbar__item">Home</a><a href="#" class="cn-topbar__item" aria-current="page">Dashboard</a><a href="#" class="cn-topbar__item">Projects</a><a href="#" class="cn-topbar__item">Tasks</a><a href="#" class="cn-topbar__item">Reporting</a><a href="#" class="cn-topbar__item">Users</a></nav><div class="cn-topbar__actions"><div class="cn-topbar__search cn-input" data-variant="default" data-size="sm"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search" aria-label="Search"><kbd class="cn-kbd">⌘K</kbd></div><a href="#" class="cn-topbar__item" data-icon-only aria-label="Settings"><svg class="cn-topbar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg></a><a href="#" class="cn-topbar__item" data-icon-only aria-label="Notifications"><svg class="cn-topbar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-topbar__count">3</span></a><button type="button" class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="online" aria-label="Account: Maya Chen" aria-haspopup="menu" tabindex="0"><span class="cn-avatar__fallback" aria-hidden="true">MC</span><span class="cn-avatar__status"></span></button></div></div></header>
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-topbar <header> — Full-width sticky bar on a surface with a gray-200 hairline below; z-index sticky.
                                • .cn-topbar__inner <div> — The 64px row: a 1280px container with 32px side padding (16 on mobile), items vertically centered, 16px gap.
                                • .cn-topbar__brand <a> — Logo mark + product name (label-lg semibold, 24px tall), links to the home view; 16px before the nav.
                                • .cn-topbar__logo <span> · optional — The 24px brand mark (an image, an inline SVG or the placeholder square).
                                • .cn-topbar__nav <nav> — Primary navigation: a row of items 2px apart. May instead hold an underline Tabs sm whose bar sits on the topbar hairline.
                                • .cn-topbar__item <a> — One nav pill: 32px tall (padding 6 × 8), radius 6, 14px semibold gray-700; gray-50 when hovered or current. Icon-only pills are 36 × 36 (data-icon-only + aria-label).
                                • .cn-topbar__icon <svg> · optional — 20px icon inside an item, fg-subtle; darkens with the label. Decorative.
                                • .cn-topbar__count <span> · optional — Unread count on an icon-only item: a 14px red circle with 10px bold white text, top-right.
                                • .cn-topbar__search <div> · optional — Global search: an Input sm with a ⌘K Kbd, max 280px, in the actions row.
                                • .cn-topbar__actions <div> — Right side, 12px gap: search, icon-only items (settings, notifications), one outline and one primary Button at most, then the account Avatar (a button with aria-haspopup="menu").
                                • .cn-topbar__secondary <div> · optional — Optional second tier under the first: a 64px row on its own hairline holding nav items, or an underline Tabs sm (data-nav="tabs") whose bar sits on the tier's hairline (padding 12 × 32, 0 bottom).

                                Props

                                propvalues (default outlined)meaning
                                variantsolid translucentsolid = opaque surface; the default for app shells. translucent = 85% surface with a 12px backdrop blur so scrolling content shows faintly through; only over a plain canvas that scrolls beneath it, never over images or maps.
                                alignstart centerstart = brand, then nav, actions pushed right (the reference "simple" header). center = nav centered between a flexible brand block and a flexible actions block (the reference "centered" header).

                                States

                                stateselectormeaning
                                current .cn-topbar__item[aria-current="page"]The destination the user is on (on the ITEM): gray-50 fill, gray-800 text, gray-500 icon; gray-100 when also hovered. (aria-current="page" on the item)
                                hover .cn-topbar__item:hoverPointer over an item: gray-50 fill, darker text and icon. (native :hover on the item)
                                focus .cn-topbar__item:focus-visible, & .cn-topbar__brand:focus-visibleKeyboard focus on an item or the brand: the 4px brand ring. (native :focus-visible)

                                Rules

                                • Exactly one Topbar per app, 64px per tier, sticky. Secondary navigation is the second tier, a Sidebar or page-level Tabs; never a third bar.
                                • The brand is the 24px mark plus the product name (label-lg semibold) and always links to the home view.
                                • Nav items are 32px pills, 2px apart, 14px semibold; labels are the product's top-level nouns ("Projects", "Reporting"), never verbs. The current one carries aria-current="page".
                                • Actions hold at most: search (280px), two icon-only items, one outline and one primary Button, then the account Avatar. Everything else goes into a Menu behind the account or a ⋯ item.
                                • Global search lives in actions as an Input sm with the ⌘K Kbd and opens the CommandPalette; it is not a page filter.
                                • Page titles, breadcrumbs, filters and tabs of the current page live in the PageHeader below the topbar, never inside it.
                                • translucent only when content scrolls beneath the bar on a plain canvas; on app shells with a Sidebar and over images use solid.
                                • Under 1024px the nav and search hide; use the Sidebar's mobile header (menu button + drawer) instead of squeezing pills.

                                Accessibility

                                • Root is a <header> (banner landmark); the nav slot is a <nav aria-label="Primary"> of plain links with aria-current="page"; a second tier gets aria-label="Secondary".
                                • Provide a "Skip to content" link as the first focusable element before the topbar, visible on focus.
                                • The brand link has an accessible name (the visible text, or aria-label when it is only a logo image).
                                • Icon-only items carry aria-label; the notification count is read with it ("Notifications, 3 unread") by adding the number to the label or an sr-only span.
                                • The account avatar is a <button aria-haspopup="menu" aria-expanded>; the menu it opens is a Menu.
                                • Because the bar is sticky, set scroll-padding-top on the html element to the total topbar height (64 or 128px).
                                Navigation

                                Tabs .cn-tabsedit

                                A tablist that switches between views of the same object, in several emphasis levels: underline (2px brand bar on a hairline), button-brand (solid action segment), button-gray (gray-50 pill), button-border (white pill in a gray track) and button-minimal (ringed white pill in a gray track); line is a vertical list with a subtle selected fill. Labels are semibold, 14px (sm, 36 / 32px) or 16px (md, 44 / 36px), with optional icons and count badges.

                                Use: Use to switch between peer views of one thing: sections of a settings page, panes of a record, views of a card. Route-level tabs are links with aria-current="page"; in-page tabs are buttons with aria-selected. Not for sequential steps (Stepper), not for filters (Tag / SegmentedControl), not for more than about eight options (SidebarNav or a Select).

                                Underline (default)16px medium labels, 36px tall; the selected tab is brand-700 with a 2px brand-600 bar on the gray-200 hairline.
                                HTML
                                <div class="cn-tabs" data-variant="underline" data-size="md" data-orientation="horizontal" data-width="hug" role="tablist" aria-label="Settings"><button type="button" role="tab" class="cn-tabs__tab" id="tab-u-0" aria-controls="panel-u-0" aria-selected="true" tabindex="0">My details</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-u-1" aria-controls="panel-u-1" aria-selected="false" tabindex="-1">Profile</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-u-2" aria-controls="panel-u-2" aria-selected="false" tabindex="-1">Password</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-u-3" aria-controls="panel-u-3" aria-selected="false" tabindex="-1">Team</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-u-4" aria-controls="panel-u-4" aria-selected="false" tabindex="-1">Plan</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-u-5" aria-controls="panel-u-5" aria-selected="false" tabindex="-1">Billing</button></div>
                                Underline sm with icons and counts14px labels, 32px tall, 16px icons. The count of the selected tab turns brand-tinted; it hides below 768px.
                                HTML
                                <div class="cn-tabs" data-variant="underline" data-size="sm" data-orientation="horizontal" data-width="hug" role="tablist" aria-label="Inbox"><button type="button" role="tab" class="cn-tabs__tab" id="tab-in-0" aria-controls="panel-in-0" aria-selected="true" tabindex="0"><svg class="cn-tabs__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg>Unread<span class="cn-tabs__count">12</span></button><button type="button" role="tab" class="cn-tabs__tab" id="tab-in-1" aria-controls="panel-in-1" aria-selected="false" tabindex="-1"><svg class="cn-tabs__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg>Mentions<span class="cn-tabs__count">3</span></button><button type="button" role="tab" class="cn-tabs__tab" id="tab-in-2" aria-controls="panel-in-2" aria-selected="false" tabindex="-1"><svg class="cn-tabs__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg>Done</button></div>
                                Button brandA solid action-colored segment marks selection; hover stays neutral.
                                HTML
                                <div class="cn-tabs" data-variant="button-brand" data-size="sm" data-orientation="horizontal" data-width="hug" role="tablist" aria-label="Settings"><button type="button" role="tab" class="cn-tabs__tab" id="tab-bb-0" aria-controls="panel-bb-0" aria-selected="true" tabindex="0">My details</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-bb-1" aria-controls="panel-bb-1" aria-selected="false" tabindex="-1">Profile</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-bb-2" aria-controls="panel-bb-2" aria-selected="false" tabindex="-1">Password</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-bb-3" aria-controls="panel-bb-3" aria-selected="false" tabindex="-1">Team</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-bb-4" aria-controls="panel-bb-4" aria-selected="false" tabindex="-1">Plan</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-bb-5" aria-controls="panel-bb-5" aria-selected="false" tabindex="-1">Billing</button></div>
                                Button graySame pill in gray-50 with gray-700 text.
                                HTML
                                <div class="cn-tabs" data-variant="button-gray" data-size="sm" data-orientation="horizontal" data-width="hug" role="tablist" aria-label="Settings"><button type="button" role="tab" class="cn-tabs__tab" id="tab-bg-0" aria-controls="panel-bg-0" aria-selected="true" tabindex="0">My details</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-bg-1" aria-controls="panel-bg-1" aria-selected="false" tabindex="-1">Profile</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-bg-2" aria-controls="panel-bg-2" aria-selected="false" tabindex="-1">Password</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-bg-3" aria-controls="panel-bg-3" aria-selected="false" tabindex="-1">Team</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-bg-4" aria-controls="panel-bg-4" aria-selected="false" tabindex="-1">Plan</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-bg-5" aria-controls="panel-bg-5" aria-selected="false" tabindex="-1">Billing</button></div>
                                Button borderA quiet track with 2px inset spacing holds a flat, outlined selected segment.
                                HTML
                                <div class="cn-tabs" data-variant="button-border" data-size="sm" data-orientation="horizontal" data-width="hug" role="tablist" aria-label="Settings"><button type="button" role="tab" class="cn-tabs__tab" id="tab-bo-0" aria-controls="panel-bo-0" aria-selected="true" tabindex="0">All</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-bo-1" aria-controls="panel-bo-1" aria-selected="false" tabindex="-1">Active</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-bo-2" aria-controls="panel-bo-2" aria-selected="false" tabindex="-1">Archived</button></div>
                                Button minimalA compact neutral track; the selected segment uses the surface fill and a control outline.
                                HTML
                                <div class="cn-tabs" data-variant="button-minimal" data-size="sm" data-orientation="horizontal" data-width="hug" role="tablist" aria-label="Settings"><button type="button" role="tab" class="cn-tabs__tab" id="tab-bm-0" aria-controls="panel-bm-0" aria-selected="true" tabindex="0">Day</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-bm-1" aria-controls="panel-bm-1" aria-selected="false" tabindex="-1">Week</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-bm-2" aria-controls="panel-bm-2" aria-selected="false" tabindex="-1">Month</button></div>
                                Line (vertical)line = subtle selected fill and action-colored text, 32px rows; any button variant becomes a column with data-orientation="vertical".
                                HTML
                                <div style="display:flex;gap:var(--cn-space-8);align-items:flex-start"><div class="cn-tabs" data-variant="line" data-size="md" data-orientation="vertical" data-width="hug" role="tablist" aria-label="Settings" aria-orientation="vertical"><button type="button" role="tab" class="cn-tabs__tab" id="tab-ln-0" aria-controls="panel-ln-0" aria-selected="true" tabindex="0">My details</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-ln-1" aria-controls="panel-ln-1" aria-selected="false" tabindex="-1">Profile</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-ln-2" aria-controls="panel-ln-2" aria-selected="false" tabindex="-1">Password</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-ln-3" aria-controls="panel-ln-3" aria-selected="false" tabindex="-1">Team</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-ln-4" aria-controls="panel-ln-4" aria-selected="false" tabindex="-1">Plan</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-ln-5" aria-controls="panel-ln-5" aria-selected="false" tabindex="-1">Billing</button></div><div class="cn-tabs" data-variant="button-brand" data-size="sm" data-orientation="vertical" data-width="hug" role="tablist" aria-label="Settings" aria-orientation="vertical"><button type="button" role="tab" class="cn-tabs__tab" id="tab-lv-0" aria-controls="panel-lv-0" aria-selected="false" tabindex="-1">My details</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-lv-1" aria-controls="panel-lv-1" aria-selected="false" tabindex="-1">Profile</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-lv-2" aria-controls="panel-lv-2" aria-selected="true" tabindex="0">Password</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-lv-3" aria-controls="panel-lv-3" aria-selected="false" tabindex="-1">Team</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-lv-4" aria-controls="panel-lv-4" aria-selected="false" tabindex="-1">Plan</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-lv-5" aria-controls="panel-lv-5" aria-selected="false" tabindex="-1">Billing</button></div></div>
                                Full widthdata-width="full": every tab takes an equal share and the underline gaps grow to 16px.
                                HTML
                                <div style="width:100%;max-width:480px"><div class="cn-tabs" data-variant="underline" data-size="sm" data-orientation="horizontal" data-width="full" role="tablist" aria-label="Settings"><button type="button" role="tab" class="cn-tabs__tab" id="tab-fw-0" aria-controls="panel-fw-0" aria-selected="false" tabindex="-1">Overview</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-fw-1" aria-controls="panel-fw-1" aria-selected="true" tabindex="0">Activity</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-fw-2" aria-controls="panel-fw-2" aria-selected="false" tabindex="-1">Files</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-fw-3" aria-controls="panel-fw-3" aria-selected="false" tabindex="-1">Notes</button></div></div>
                                Pill and enclosed (kept for existing markup)pill is the same look as button-border, with token-defined corners and 2px inset spacing. enclosed is the folder tab for editor panes.
                                HTML
                                <div style="display:flex;flex-direction:column;gap:var(--cn-space-6);align-items:flex-start"><div class="cn-tabs" data-variant="pill" data-size="md" data-orientation="horizontal" data-width="hug" role="tablist" aria-label="Settings"><button type="button" role="tab" class="cn-tabs__tab" id="tab-pl-0" aria-controls="panel-pl-0" aria-selected="true" tabindex="0">Overview</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-pl-1" aria-controls="panel-pl-1" aria-selected="false" tabindex="-1">Activity</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-pl-2" aria-controls="panel-pl-2" aria-selected="false" tabindex="-1">Files</button></div><div class="cn-tabs" data-variant="enclosed" data-size="md" data-orientation="horizontal" data-width="hug" role="tablist" aria-label="Settings"><button type="button" role="tab" class="cn-tabs__tab" id="tab-en-0" aria-controls="panel-en-0" aria-selected="false" tabindex="-1">Preview</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-en-1" aria-controls="panel-en-1" aria-selected="true" tabindex="0">HTML</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-en-2" aria-controls="panel-en-2" aria-selected="false" tabindex="-1">CSS</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-en-3" aria-controls="panel-en-3" aria-selected="false" tabindex="-1">React</button></div></div>
                                With panelThe panel is a sibling after the tablist with padding-top 24px.

                                4 steps over 9 days. The next email goes out Thursday at 09:00 to 38 customers.

                                HTML
                                <div style="width:100%;max-width:560px"><div class="cn-tabs" data-variant="underline" data-size="md" data-orientation="horizontal" data-width="hug" role="tablist" aria-label="Settings"><button type="button" role="tab" class="cn-tabs__tab" id="tab-pn-0" aria-controls="panel-pn-0" aria-selected="true" tabindex="0">Steps</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-pn-1" aria-controls="panel-pn-1" aria-selected="false" tabindex="-1">Replies</button><button type="button" role="tab" class="cn-tabs__tab" id="tab-pn-2" aria-controls="panel-pn-2" aria-selected="false" tabindex="-1">Settings</button></div><div class="cn-tabs__panel" role="tabpanel" id="panel-pn-0" aria-labelledby="tab-pn-0" tabindex="0"><p class="cn-text-body-md">4 steps over 9 days. The next email goes out Thursday at 09:00 to 38 customers.</p></div></div>
                                Route tabs with a disabled tabLinks with aria-current="page" when each tab is a URL; no tablist role. The disabled tab keeps its label at 50% opacity.
                                HTML
                                <div class="cn-tabs" data-variant="underline" data-size="md" data-orientation="horizontal" data-width="hug" aria-label="Reports"><a href="#" class="cn-tabs__tab" aria-current="page">Customers</a><a href="#" class="cn-tabs__tab">Invoices</a><a href="#" class="cn-tabs__tab">Files</a><a class="cn-tabs__tab" aria-disabled="true" title="Available on the Business plan">Attribution</a></div>

                                Matrix

                                variant ↓ / size →smmd
                                underline
                                pill
                                enclosed
                                button-brand
                                button-gray
                                button-border
                                button-minimal
                                line
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-tabs <div> — The tablist (role="tablist", aria-label). Flex row of tabs (column when data-orientation="vertical"); carries the bottom hairline for underline, or the gray-50 track for button-border / button-minimal / pill.
                                • .cn-tabs__tab <button> — One tab: <button role="tab"> for in-page views, <a aria-current="page"> for routes. Semibold text-quaternary label, radius 6, gap 8; holds text, optional icon, optional count badge.
                                • .cn-tabs__icon <svg> · optional — Optional leading icon, 16px (sm) or 20px (md), fg-subtle. If one tab has an icon, all tabs in the list do. Decorative.
                                • .cn-tabs__count <span> · optional — Optional count after the label: a 22px badge (12px medium) in gray; turns brand-tinted on the selected tab of underline / line / button-brand, stays a white "modern" badge elsewhere. Hidden under 768px. Only for numbers that call for action.
                                • .cn-tabs__panel <div> · optional — The tabpanel (role="tabpanel", aria-labelledby the tab). A sibling AFTER the tablist, never inside it; padding-top separates it from the list.

                                Props

                                propvalues (default outlined)meaning
                                variantunderline pill enclosed button-brand button-gray button-border button-minimal lineunderline = the page-section default: 2px brand-600 bar under the selected tab on a full-width gray-200 hairline, brand-700 text when selected . button-brand = solid action segment with brand-700 text (settings sub-navigation, filters in a card). button-gray = gray-50 pill with gray-700 text (quiet toolbars). button-border = white pill with a fine outline inside a gray-50 track with a gray-200 ring (view switch in a card header). button-minimal = white pill with a control outline inside a gray-50 track, radius 8 (dense toolbars). line = vertical list with a subtle selected fill and action-colored text (settings side navigation; set data-orientation="vertical"). pill = the older name of button-border, kept for existing markup. enclosed = folder tabs that merge into the panel below (editor panes only).
                                sizesm mdmd = 16px medium labels: 44px button tabs, 36px underline tabs, 32px line tabs; the default for page sections and settings. sm = 14px medium labels: 36px button tabs, 32px underline tabs, 24px line tabs; inside cards, drawers and dense toolbars.
                                orientationhorizontal verticalhorizontal = a row (every variant except line). vertical = a column, 8px between tabs, labels aligned to the start; use with line, button-brand, button-gray, button-border or button-minimal for a settings side navigation.
                                widthhug fullhug = tabs take the width of their labels (default). full = the list spans its container and every tab takes an equal share (mobile, card headers with 2–4 tabs); underline gaps grow from 12 to 16px.

                                States

                                stateselectormeaning
                                current .cn-tabs__tab[aria-selected="true"], & .cn-tabs__tab[aria-current="page"]The selected tab (lives on the TAB, not the root). underline: action-colored text and bottom rule; line: action-colored text and subtle fill; button-brand: action fill and on-action text; button-gray: gray-50 fill; button-border: white pill with a fine outline; button-minimal: white pill with a control outline; enclosed: surface fill with an open bottom. (aria-selected="true" on a role="tab" button, or aria-current="page" on a link tab)
                                hover .cn-tabs__tab:hoverPointer over a tab: a subtle surface change; the selection bar stays on the current tab. (native :hover on the tab)
                                focus .cn-tabs__tab:focus-visibleKeyboard focus on a tab: the token-defined focus ring around that tab. (native :focus-visible on the tab)
                                disabled .cn-tabs__tab:disabled, & .cn-tabs__tab[aria-disabled="true"]Tab cannot be chosen (feature off, no data yet). 50% opacity, no hover; keep the label so users know the view exists. (disabled or aria-disabled="true" on the tab)

                                Rules

                                • 2–8 tabs. With one there is nothing to switch; beyond eight use SidebarNav or a Select.
                                • Labels are 1–3 words in sentence case, nouns ("My details", "Billing"). Never verbs, never trailing punctuation.
                                • Exactly one tab is selected at all times; the first one by default. Never start with nothing selected.
                                • Heights come only from size: md = 44px button tabs / 36px underline / 32px line; sm = 36 / 32 / 24. Never add padding to change them.
                                • Counts are the 22px badge (count part); only for numbers that ask for action (unread, waiting, failed). Totals belong in the panel.
                                • Icons are all-or-none within one list, leading, 16px at sm and 20px at md, fg-subtle at rest and brand or ink when selected.
                                • underline for page sections and settings pages; button-brand or button-gray for a sub-navigation inside a page; button-border / button-minimal for a view switch inside a card header or toolbar; line (vertical) for a settings side navigation; enclosed only for editor panes with a visible panel border.
                                • Hover uses a quiet fill. The selection rule or solid segment appears only on the current tab.
                                • Tabs switch views of the same object. Different pages are links with aria-current="page"; a sequential flow is a Stepper.
                                • Never nest a tablist inside a tabpanel. A second level of navigation is a SidebarNav or a button-* Tabs inside the card, not tabs under tabs.
                                • Tabs never wrap onto a second line. On narrow screens use data-width="full" with ≤ 4 tabs, or let the tablist scroll horizontally. Focus stays inside the tab so it remains visible in the scroll area.

                                Accessibility

                                • In-page tabs: root role="tablist" with aria-label, tabs are <button role="tab" aria-selected aria-controls>, panels are role="tabpanel" aria-labelledby, tabindex="0".
                                • Roving tabindex: only the selected tab is in the tab order (tabindex="0"); Left/Right (Up/Down when vertical) move focus, Home/End jump, Enter/Space or automatic activation selects.
                                • Route tabs are plain <a href> with aria-current="page" inside a <nav>; do NOT add role="tab" to links that navigate.
                                • Disabled tabs use aria-disabled="true" (stay perceivable) and a title or Tooltip with the reason.
                                • Selection is conveyed by text color and the bar or pill, never by color alone; the count keeps its number in text and hides only visually on mobile.
                                • Vertical lists set aria-orientation="vertical" on the tablist so arrow-key direction matches.
                                Navigation

                                SidebarNav .cn-sidebar-navedit

                                A grouped navigation rail: a 280px column of 36px link rows (14px semibold, 20px icon, radius 6) grouped with subheadings or dividers, a quiet hover fill and action-colored text on the current destination, with count badges and collapsible children.

                                Use: Use as the nav list of a Sidebar or on its own for the second level of navigation inside a product area (settings sections, a workspace's views). Items are answers to what the user wants to do, grouped by job, never a mirror of the data model. For 3–6 top-level destinations use the Topbar instead.

                                Simple listComponent preview36px rows, 14px semibold, 20px icons; Dashboard is current (gray-50), Tasks carries a 22px count badge.
                                HTML
                                <nav class="cn-sidebar-nav" data-variant="default" data-size="md" aria-label="Main"><div class="cn-sidebar-nav__group" role="group"><a href="#" class="cn-sidebar-nav__item"><svg class="cn-sidebar-nav__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg><span class="cn-sidebar-nav__label">Home</span></a><a href="#" class="cn-sidebar-nav__item" aria-current="page"><svg class="cn-sidebar-nav__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><span class="cn-sidebar-nav__label">Dashboard</span></a><a href="#" class="cn-sidebar-nav__item"><svg class="cn-sidebar-nav__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-sidebar-nav__label">Projects</span></a><a href="#" class="cn-sidebar-nav__item"><svg class="cn-sidebar-nav__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-sidebar-nav__label">Tasks</span><span class="cn-sidebar-nav__count">10</span></a><a href="#" class="cn-sidebar-nav__item"><svg class="cn-sidebar-nav__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-sidebar-nav__label">Reporting</span></a><a href="#" class="cn-sidebar-nav__item"><svg class="cn-sidebar-nav__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><span class="cn-sidebar-nav__label">Users</span></a></div></nav>
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-sidebar-nav <nav> — The column: 280px wide, 20 × 16px padding, groups stacked. Has aria-label.
                                • .cn-sidebar-nav__group <div> — A set of related items with role="group" (or a <details> for a collapsible parent); rows 2px apart, 16px above each group after the first.
                                • .cn-sidebar-nav__group-label <div> · optional — Subheading naming the group in 12px semibold fg-subtle, 8px inset: "General", "Workspace". Referenced by the group's aria-labelledby.
                                • .cn-sidebar-nav__item <a> — One destination: a 36px link row (or <summary> for a collapsible parent), padding 8, radius 6, 14px semibold gray-700; holds icon + label + optional count or chevron. data-level="2" indents a child row to 40px.
                                • .cn-sidebar-nav__icon <svg> · optional — 20px leading icon, fg-subtle at rest, one step darker when hovered or current. All items in a group have one, or none do.
                                • .cn-sidebar-nav__label <span> — The destination name, 1–2 words. Fills the row and truncates with an ellipsis.
                                • .cn-sidebar-nav__count <span> · optional — Trailing count in a 22px gray badge (12px medium, gray-50, gray-200 ring), 12px after the label. Only for numbers that need action, never totals.
                                • .cn-sidebar-nav__chevron <svg> · optional — 16px chevron-down at the end of a collapsible parent; flips when the <details> is open.
                                • .cn-sidebar-nav__divider <hr> · optional — 1px gray-200 rule between groups (the "dividers" layout), 8px above and below.

                                Props

                                propvalues (default outlined)meaning
                                sizesm mdmd = 36px rows, 14px semibold, 20px icons; the default. sm = 32px rows (padding 6 × 8), 14px semibold, 16px icons for dense admin rails and secondary panels.
                                variantdefault insetdefault = transparent on the surface; hovered and current rows get the gray-50 fill (the default). inset = the whole nav is a gray-50 rail with a hairline on its trailing edge, and the current item uses a surface fill and action-colored text.

                                States

                                stateselectormeaning
                                hover .cn-sidebar-nav__item:hoverPointer over an item (lives on the ITEM, not the root): gray-50 fill, gray-800 label, gray-500 icon. (native :hover on the item)
                                current .cn-sidebar-nav__item[aria-current="page"]The destination the user is on: gray-50 fill (gray-100 when hovered), ink label, darker icon (inset: surface fill with action-colored text). Exactly one per nav. (aria-current="page" on the item)
                                focus .cn-sidebar-nav__item:focus-visibleKeyboard focus on an item: the token-defined focus ring around the row. (native :focus-visible on the item)
                                disabled .cn-sidebar-nav__item[aria-disabled="true"]Destination not available on this plan or not yet set up. 50% opacity, no hover; keep the label so the user knows it exists. (aria-disabled="true" on the item (omit href))

                                Rules

                                • Max 7 items per group and max 4 groups visible. If the nav needs more, the information architecture is wrong, not the component.
                                • Never one item per database table. Items are jobs and destinations ("Dashboard", "Reporting", "Billing"), grouped by who uses them and when.
                                • Labels are 1–2 words, sentence case, nouns. No verbs ("Manage members" → "Members"), no product jargon.
                                • Rows are 36px (sm 32px) with 8px padding and 2px between them; never change the height with padding.
                                • Icons are all-or-none within a group, 20px (16px at sm), fg-subtle; never emoji, never colored.
                                • Counts use the 22px badge and only for what needs action (unread, pending approvals, failed runs), never totals; hide the count at 0.
                                • Exactly one item carries aria-current="page" at any time, the deepest matching route; a collapsible parent stays open while a child is current.
                                • Width is fixed at 280px; the content never widens it. Long labels truncate with an ellipsis and get a title attribute.
                                • Two levels at most: a collapsible parent and its children. Deeper sections are Tabs on the page.
                                • The first group may go unlabeled (the home set); later groups get a subheading or a divider, not both.

                                Accessibility

                                • Root is <nav aria-label="…"> naming the area ("Main", "Settings"); each group is role="group" with aria-labelledby pointing at its subheading.
                                • Items are real <a href> links; aria-current="page" marks the current destination and is the only selection state.
                                • Collapsible parents are <details>/<summary> so they work without JS; the chevron is decorative.
                                • Disabled items use aria-disabled="true", drop the href, and explain why in a title or Tooltip; they stay in the DOM so the structure is stable.
                                • Counts are read as part of the link text; add sr-only context when the number is ambiguous ("10 open tasks").
                                • Keyboard: Tab moves through items in order; no roving tabindex or arrow keys (this is a list of links, not a menu).
                                Navigation

                                Breadcrumb .cn-breadcrumbedit

                                The path to the current page in the reference's three trail types: 14px medium links separated by 16px gray-300 chevrons (or slashes), ending in the current page one step darker; plain text (20px), text on a hairline (36px) or 28px button chips. Sits above the page title.

                                Use: Use on pages three or more levels deep (Settings → Team → Roles) so the user can go up one level with one click. Not for top-level pages, not for wizard progress (Stepper), never inside the Topbar.

                                Text (default)Ancestors gray-600, the current page one step darker; 16px gray-300 chevrons.
                                HTML
                                <nav class="cn-breadcrumb" data-variant="text" data-divider="chevron" data-size="md" aria-label="Breadcrumb"><ol class="cn-breadcrumb__list"><li class="cn-breadcrumb__item"><a href="#" class="cn-breadcrumb__link">Settings</a></li><li class="cn-breadcrumb__item"><svg class="cn-breadcrumb__separator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg><a href="#" class="cn-breadcrumb__link">Team</a></li><li class="cn-breadcrumb__item"><svg class="cn-breadcrumb__separator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg><span class="cn-breadcrumb__current" aria-current="page">Roles and permissions</span></li></ol></nav>
                                With home iconA 20px home icon replaces the first label (aria-label="Home").
                                HTML
                                <nav class="cn-breadcrumb" data-variant="text" data-divider="chevron" data-size="md" aria-label="Breadcrumb"><ol class="cn-breadcrumb__list"><li class="cn-breadcrumb__item"><a href="#" class="cn-breadcrumb__link" aria-label="Home"><svg class="cn-breadcrumb__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg></a></li><li class="cn-breadcrumb__item"><svg class="cn-breadcrumb__separator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg><a href="#" class="cn-breadcrumb__link">Projects</a></li><li class="cn-breadcrumb__item"><svg class="cn-breadcrumb__separator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg><a href="#" class="cn-breadcrumb__link">Lumen website</a></li><li class="cn-breadcrumb__item"><svg class="cn-breadcrumb__separator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg><span class="cn-breadcrumb__current" aria-current="page">Design</span></li></ol></nav>
                                Slash dividerdata-divider="slash" for denser, path-like trails.
                                HTML
                                <nav class="cn-breadcrumb" data-variant="text" data-divider="slash" data-size="md" aria-label="Breadcrumb"><ol class="cn-breadcrumb__list"><li class="cn-breadcrumb__item"><a href="#" class="cn-breadcrumb__link" aria-label="Home"><svg class="cn-breadcrumb__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg></a></li><li class="cn-breadcrumb__item"><span class="cn-breadcrumb__separator" aria-hidden="true">/</span><a href="#" class="cn-breadcrumb__link">Projects</a></li><li class="cn-breadcrumb__item"><span class="cn-breadcrumb__separator" aria-hidden="true">/</span><span class="cn-breadcrumb__current" aria-current="page">Lumen website</span></li></ol></nav>
                                Text with line36px tall on a full-width hairline; use above a title that has no divider of its own.
                                HTML
                                <div style="width:100%;max-width:560px"><nav class="cn-breadcrumb" data-variant="line" data-divider="chevron" data-size="md" aria-label="Breadcrumb"><ol class="cn-breadcrumb__list"><li class="cn-breadcrumb__item"><a href="#" class="cn-breadcrumb__link" aria-label="Home"><svg class="cn-breadcrumb__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg></a></li><li class="cn-breadcrumb__item"><svg class="cn-breadcrumb__separator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg><a href="#" class="cn-breadcrumb__link">Customers</a></li><li class="cn-breadcrumb__item"><svg class="cn-breadcrumb__separator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg><span class="cn-breadcrumb__current" aria-current="page">Sofia Almeida</span></li></ol></nav></div>
                                Button chips28px chips, radius 6; the current page is a gray-50 chip and hovered ancestors light up.
                                HTML
                                <nav class="cn-breadcrumb" data-variant="button" data-divider="chevron" data-size="md" aria-label="Breadcrumb"><ol class="cn-breadcrumb__list"><li class="cn-breadcrumb__item"><a href="#" class="cn-breadcrumb__link" aria-label="Home"><svg class="cn-breadcrumb__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg></a></li><li class="cn-breadcrumb__item"><svg class="cn-breadcrumb__separator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg><a href="#" class="cn-breadcrumb__link">Invoices</a></li><li class="cn-breadcrumb__item"><svg class="cn-breadcrumb__separator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg><span class="cn-breadcrumb__current" aria-current="page">INV-0042</span></li></ol></nav>
                                Collapsed middleDeeper than four levels: keep root, the parent and the current page; the ellipsis opens a Menu with the rest.
                                HTML
                                <nav class="cn-breadcrumb" data-variant="text" data-divider="chevron" data-size="md" aria-label="Breadcrumb"><ol class="cn-breadcrumb__list"><li class="cn-breadcrumb__item"><a href="#" class="cn-breadcrumb__link" aria-label="Home"><svg class="cn-breadcrumb__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg></a></li><li class="cn-breadcrumb__item"><svg class="cn-breadcrumb__separator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg><button type="button" class="cn-breadcrumb__ellipsis" aria-label="Show 2 hidden levels" aria-haspopup="menu" aria-expanded="false">…</button></li><li class="cn-breadcrumb__item"><svg class="cn-breadcrumb__separator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg><a href="#" class="cn-breadcrumb__link">Q3 launch</a></li><li class="cn-breadcrumb__item"><svg class="cn-breadcrumb__separator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg><span class="cn-breadcrumb__current" aria-current="page">Design review</span></li></ol></nav>
                                Small, inside a drawer header
                                HTML
                                <nav class="cn-breadcrumb" data-variant="text" data-divider="chevron" data-size="sm" aria-label="Breadcrumb"><ol class="cn-breadcrumb__list"><li class="cn-breadcrumb__item"><a href="#" class="cn-breadcrumb__link">Settings</a></li><li class="cn-breadcrumb__item"><svg class="cn-breadcrumb__separator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg><a href="#" class="cn-breadcrumb__link">Team</a></li><li class="cn-breadcrumb__item"><svg class="cn-breadcrumb__separator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg><span class="cn-breadcrumb__current" aria-current="page">Roles and permissions</span></li></ol></nav>
                                Long names truncateLinks cut at 24ch, the current page at 32ch; the full text goes in a title attribute.
                                HTML
                                <nav class="cn-breadcrumb" data-variant="text" data-divider="chevron" data-size="md" aria-label="Breadcrumb"><ol class="cn-breadcrumb__list"><li class="cn-breadcrumb__item"><a href="#" class="cn-breadcrumb__link" aria-label="Home"><svg class="cn-breadcrumb__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg></a></li><li class="cn-breadcrumb__item"><svg class="cn-breadcrumb__separator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg><a href="#" class="cn-breadcrumb__link">Enterprise procurement pipeline for the northern region</a></li><li class="cn-breadcrumb__item"><svg class="cn-breadcrumb__separator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg><span class="cn-breadcrumb__current" aria-current="page">Annual supply agreement 2026 with Daniel Costa's team</span></li></ol></nav>

                                Matrix

                                variant ↓ / size →smmd
                                text
                                line
                                button
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-breadcrumb <nav> — The landmark: <nav aria-label="Breadcrumb">, 14px medium, fg-muted.
                                • .cn-breadcrumb__list <ol> — Ordered list of levels, root first. Flex row, 8px gaps, wraps on narrow screens.
                                • .cn-breadcrumb__item <li> — One level: an optional separator followed by a link, the ellipsis button, or the current page.
                                • .cn-breadcrumb__link <a> — An ancestor page, fg-muted; ink on hover (gray-50 chip in the button type). Truncates at 24 characters.
                                • .cn-breadcrumb__icon <svg> · optional — Optional 20px home icon as the first link (with aria-label="Home"), fg-subtle.
                                • .cn-breadcrumb__separator <svg> — 16px gray-300 chevron (or a "/" span) before every item except the first. aria-hidden.
                                • .cn-breadcrumb__ellipsis <button> · optional — The "…" that stands in for collapsed middle levels; opens a Menu listing them.
                                • .cn-breadcrumb__current <span> — The page the user is on: one step darker than the links, medium, aria-current="page". Not a link.

                                Props

                                propvalues (default outlined)meaning
                                varianttext line buttontext = plain 20px trail (page headers, the default). line = the trail sits on a full-width gray-200 hairline, 36px tall (above a page title without its own divider). button = each level is a 28px chip (radius 6, padding 4 × 8); the current one is filled gray-50 (record headers, drawers).
                                dividerchevron slashchevron = 16px gray-300 chevron-right (default). slash = a "/" in gray-300 (denser, developer-facing paths).
                                sizesm mdmd = 14px medium (text-sm); the page-header default. sm = 12px medium for drawers, dialogs and card headers.

                                States

                                stateselectormeaning
                                hover .cn-breadcrumb__link:hoverPointer over an ancestor link (on the LINK, not the root): ink text; in the button type also a gray-50 chip. (native :hover on the link)
                                focus .cn-breadcrumb__link:focus-visible, & .cn-breadcrumb__ellipsis:focus-visibleKeyboard focus on a link or the ellipsis: the 4px brand ring. (native :focus-visible)
                                current [aria-current="page"]The last item: one step darker, medium, no hover, not a link (button type: gray-50 chip). (aria-current="page" on the current span)

                                Rules

                                • Max 4 visible levels. Deeper paths collapse the middle into an ellipsis that opens a Menu of the hidden levels.
                                • Labels are the exact page titles of the ancestors (same text as their h1), not shortened synonyms.
                                • The last item is the current page: darker, not a link, aria-current="page". Never end with a separator.
                                • Never show a one-level breadcrumb ("Home" alone). Below three levels, skip the breadcrumb entirely.
                                • Place it in the page header directly above the title, 8px apart. Never in the Topbar and never at the bottom of the page.
                                • Separators are the 16px gray-300 chevron, or the slash for path-like trails; never arrows, dots or both in one app.
                                • text in page headers; line when the header has no divider of its own; button in record headers and drawers where the trail is also a filter.
                                • sm only inside drawers, dialogs and card headers; md everywhere else.
                                • Truncate long names with an ellipsis and provide the full name in a title; never wrap a single label across lines.

                                Accessibility

                                • Root is <nav aria-label="Breadcrumb"> containing an <ol>; order conveys hierarchy.
                                • The current page has aria-current="page" and is not a link (or is a link only if it reloads the same page).
                                • Separators are aria-hidden="true"; the chevron or slash is decoration, the list order is the semantics.
                                • A home icon link carries aria-label="Home"; the icon itself is aria-hidden.
                                • The ellipsis is a <button aria-label="Show N hidden levels" aria-haspopup="menu" aria-expanded>, not plain text.
                                • Truncated labels keep their full text in a title attribute and in the accessible name.
                                Navigation

                                Pagination .cn-paginationedit

                                Page controls of the reference: 36px page numbers (14px medium, gray-50 when current) with ellipses, secondary-style Previous / Next buttons at the ends on a hairline; a minimal "Page 1 of 10" center; a segmented button group; dots and lines for carousels.

                                Use: Use under a table or list whose total is known and larger than one page (page-default under page-level tables, minimal or button-group in a table card footer). Use simple or minimal when the total is unknown or the space is narrow. dots and line are for carousels and onboarding slides, never for data.

                                Page defaultPrevious and Next at the ends (secondary-style, 36px), numbers centered 2px apart on a hairline with 20px of padding. Page 1 is current in gray-50.
                                HTML
                                <div style="width:100%;max-width:720px"><nav class="cn-pagination" data-variant="page-default" data-size="md" data-shape="square" data-align="center" aria-label="Pagination"><button type="button" class="cn-pagination__prev" aria-label="Previous page" disabled><svg class="cn-pagination__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M13 8H3M7 4L3 8l4 4"/></svg><span>Previous</span></button><div class="cn-pagination__pages"><button type="button" class="cn-pagination__item" aria-current="page">1</button><button type="button" class="cn-pagination__item">2</button><button type="button" class="cn-pagination__item">3</button><span class="cn-pagination__ellipsis" aria-hidden="true">…</span><button type="button" class="cn-pagination__item">8</button><button type="button" class="cn-pagination__item">9</button><button type="button" class="cn-pagination__item">10</button></div><button type="button" class="cn-pagination__next" aria-label="Next page"><span>Next</span><svg class="cn-pagination__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg></button></nav></div>
                                Page default, rounded, middle pagedata-shape="round": fully rounded items. Seven slots: first, ellipsis, current ±1, ellipsis, last.
                                HTML
                                <div style="width:100%;max-width:720px"><nav class="cn-pagination" data-variant="page-default" data-size="md" data-shape="round" data-align="center" aria-label="Pagination"><button type="button" class="cn-pagination__prev" aria-label="Previous page"><svg class="cn-pagination__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M13 8H3M7 4L3 8l4 4"/></svg><span>Previous</span></button><div class="cn-pagination__pages"><button type="button" class="cn-pagination__item">1</button><span class="cn-pagination__ellipsis" aria-hidden="true">…</span><button type="button" class="cn-pagination__item">5</button><button type="button" class="cn-pagination__item" aria-current="page">6</button><button type="button" class="cn-pagination__item">7</button><span class="cn-pagination__ellipsis" aria-hidden="true">…</span><button type="button" class="cn-pagination__item">10</button></div><button type="button" class="cn-pagination__next" aria-label="Next page"><span>Next</span><svg class="cn-pagination__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg></button></nav></div>
                                Minimal centerPrevious, "Page 1 of 10" and Next; the reference minimal center type for table card footers.
                                HTML
                                <div style="width:100%;max-width:720px"><nav class="cn-pagination" data-variant="minimal" data-size="md" data-shape="square" data-align="center" aria-label="Pagination"><button type="button" class="cn-pagination__prev" aria-label="Previous page" disabled><svg class="cn-pagination__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M13 8H3M7 4L3 8l4 4"/></svg><span>Previous</span></button><span class="cn-pagination__summary">Page 1 of 10</span><button type="button" class="cn-pagination__next" aria-label="Next page"><span>Next</span><svg class="cn-pagination__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg></button></nav></div>
                                Minimal, right aligned, smallThe summary on the left, two 32px icon-only chevron buttons on the right (data-icon-only).
                                HTML
                                <div style="width:100%;max-width:720px"><nav class="cn-pagination" data-variant="minimal" data-size="sm" data-shape="square" data-align="right" aria-label="Pagination"><span class="cn-pagination__summary">Page 3 of 10</span><button type="button" class="cn-pagination__prev" aria-label="Previous page" data-icon-only><svg class="cn-pagination__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 4l-4 4 4 4"/></svg></button><button type="button" class="cn-pagination__next" aria-label="Next page" data-icon-only><svg class="cn-pagination__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button></nav></div>
                                Button groupOne 36px segmented control with shared gray-300 rings and shadow-xs; the current page is gray-50.
                                HTML
                                <div style="width:100%;max-width:720px"><nav class="cn-pagination" data-variant="button-group" data-size="md" data-shape="square" data-align="right" aria-label="Pagination"><div class="cn-pagination__pages"><button type="button" class="cn-pagination__item" aria-current="page">1</button><button type="button" class="cn-pagination__item">2</button><button type="button" class="cn-pagination__item">3</button><span class="cn-pagination__ellipsis" aria-hidden="true">…</span><button type="button" class="cn-pagination__item">8</button><button type="button" class="cn-pagination__item">9</button><button type="button" class="cn-pagination__item">10</button></div></nav></div>
                                Default (inline) and simpledefault keeps everything in one inline row; simple hides the page items for cursor APIs and unknown totals.
                                HTML
                                <div style="display:flex;flex-direction:column;gap:var(--cn-space-4)"><nav class="cn-pagination" data-variant="default" data-size="md" data-shape="square" data-align="center" aria-label="Pagination"><span class="cn-pagination__summary">1–10 of 240</span><button type="button" class="cn-pagination__prev" aria-label="Previous page" disabled data-icon-only><svg class="cn-pagination__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 4l-4 4 4 4"/></svg></button><div class="cn-pagination__pages"><button type="button" class="cn-pagination__item" aria-current="page">1</button><button type="button" class="cn-pagination__item">2</button><button type="button" class="cn-pagination__item">3</button><span class="cn-pagination__ellipsis" aria-hidden="true">…</span><button type="button" class="cn-pagination__item">24</button></div><button type="button" class="cn-pagination__next" aria-label="Next page" data-icon-only><svg class="cn-pagination__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button></nav><nav class="cn-pagination" data-variant="simple" data-size="md" data-shape="square" data-align="center" aria-label="Pagination"><span class="cn-pagination__summary">Showing 20 results</span><button type="button" class="cn-pagination__prev" aria-label="Previous page" data-icon-only><svg class="cn-pagination__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 4l-4 4 4 4"/></svg></button><button type="button" class="cn-pagination__next" aria-label="Next page" data-icon-only><svg class="cn-pagination__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button></nav></div>
                                Dots and lines (carousels)10px dots (8px at sm) and 40 × 8 lines, gray-200 at rest and brand-600 when current; data-framed puts them on a white ringed pill.
                                HTML
                                <div style="display:flex;flex-direction:column;gap:var(--cn-space-6);align-items:flex-start"><nav class="cn-pagination" data-variant="dots" data-size="md" data-shape="square" data-align="left" aria-label="Pagination"><div class="cn-pagination__pages"><button type="button" class="cn-pagination__dot" aria-label="Slide 1"></button><button type="button" class="cn-pagination__dot" aria-label="Slide 2" aria-current="true"></button><button type="button" class="cn-pagination__dot" aria-label="Slide 3"></button><button type="button" class="cn-pagination__dot" aria-label="Slide 4"></button><button type="button" class="cn-pagination__dot" aria-label="Slide 5"></button></div></nav><nav class="cn-pagination" data-variant="line" data-size="md" data-shape="square" data-align="left" aria-label="Pagination"><div class="cn-pagination__pages"><button type="button" class="cn-pagination__line" aria-label="Step 1"></button><button type="button" class="cn-pagination__line" aria-label="Step 2"></button><button type="button" class="cn-pagination__line" aria-label="Step 3" aria-current="true"></button><button type="button" class="cn-pagination__line" aria-label="Step 4"></button></div></nav><nav class="cn-pagination" data-variant="dots" data-size="sm" data-shape="square" data-align="left" aria-label="Pagination" data-framed><div class="cn-pagination__pages"><button type="button" class="cn-pagination__dot" aria-label="Slide 1" aria-current="true"></button><button type="button" class="cn-pagination__dot" aria-label="Slide 2"></button><button type="button" class="cn-pagination__dot" aria-label="Slide 3"></button><button type="button" class="cn-pagination__dot" aria-label="Slide 4"></button></div></nav></div>
                                Pages as linksWhen the page is in the URL, every control is an <a href> so pages are shareable and crawlable.
                                HTML
                                <div style="width:100%;max-width:720px"><nav class="cn-pagination" data-variant="page-default" data-size="md" data-shape="square" data-align="center" aria-label="Pagination"><a href="?page=1" class="cn-pagination__prev" aria-label="Previous page"><svg class="cn-pagination__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M13 8H3M7 4L3 8l4 4"/></svg><span>Previous</span></a><div class="cn-pagination__pages"><a href="?page=1" class="cn-pagination__item">1</a><a href="?page=2" class="cn-pagination__item" aria-current="page">2</a><a href="?page=3" class="cn-pagination__item">3</a><a href="?page=4" class="cn-pagination__item">4</a><a href="?page=5" class="cn-pagination__item">5</a></div><a href="?page=3" class="cn-pagination__next" aria-label="Next page"><span>Next</span><svg class="cn-pagination__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg></a></nav></div>

                                Matrix

                                variant ↓ / size →smmd
                                default
                                simple
                                page-default
                                minimal
                                button-group
                                dots
                                line
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-pagination <nav> — <nav aria-label="Pagination">: a flex row, space-between, 12px gap; page-default and minimal add the hairline above with 20px of padding.
                                • .cn-pagination__summary <span> · optional — The position in words, 14px medium gray-700 with tabular numbers: "Page 1 of 10" (minimal) or "1–10 of 240" (default).
                                • .cn-pagination__pages <div> · optional — The centered row of page items, 2px apart (0 in button-group, 8 for dots and lines).
                                • .cn-pagination__prev <button> — Previous: a 36px secondary-style button (gray-300 ring, shadow-xs) with a 20px arrow and the word "Previous"; data-icon-only makes it a 36px square. Disabled on the first page, never hidden.
                                • .cn-pagination__item <button> — One page number, 36 × 36, 14px medium gray-500, radius 8 (or full); gray-50 with gray-700 text when hovered or current (aria-current="page").
                                • .cn-pagination__ellipsis <span> · optional — The "…" standing in for skipped pages, 36 × 36, gray-600, aria-hidden.
                                • .cn-pagination__next <button> — Next: the mirror of prev. Disabled on the last page.
                                • .cn-pagination__icon <svg> — 20px arrow or chevron inside prev / next. Decorative.
                                • .cn-pagination__dot <button> · optional — An 8px gray-200 dot (dots variant); brand-600 when current. aria-label "Slide N".
                                • .cn-pagination__line <button> · optional — A 40 × 6 gray-200 bar (line variant); brand-600 when current.

                                Props

                                propvalues (default outlined)meaning
                                variantdefault simple page-default minimal button-group dots linedefault = summary, prev, page items and next in one row (inline under a table). simple = summary + prev / next only, for unknown totals and narrow layouts. page-default = the reference page pagination: Previous at the left, numbers centered, Next at the right, on a hairline with 20px padding. minimal = Previous, "Page 1 of 10" and Next, same hairline (table card footers). button-group = the page items as one 36px segmented control with shared rings. dots = 8px dots for carousels. line = 40 × 6 bars for onboarding steps.
                                sizesm mdmd = 36px items and buttons (the reference). sm = 32px items and buttons for dense card footers and drawers.
                                shapesquare roundsquare = radius 8 on page items (default). round = fully rounded page items (the reference rounded={true}).
                                alignleft center rightWhere the pages or the summary sit in minimal, button-group, dots and line: left, center (default) or right. page-default is always Previous | pages | Next.

                                States

                                stateselectormeaning
                                current .cn-pagination__item[aria-current="page"], & .cn-pagination__dot[aria-current="true"], & .cn-pagination__line[aria-current="true"]The page being shown (on the ITEM, not the root): gray-50 fill with gray-700 text; dots and lines turn brand-600. (aria-current="page" on the page item, aria-current="true" on a dot or line)
                                hover .cn-pagination__item:hover, & .cn-pagination__prev:hover, & .cn-pagination__next:hoverPointer over a page or a button: gray-50 fill and gray-700 text (the same look as current). (native :hover)
                                focus .cn-pagination__item:focus-visible, & .cn-pagination__prev:focus-visible, & .cn-pagination__next:focus-visibleKeyboard focus: the 4px brand ring around that control. (native :focus-visible)
                                disabled .cn-pagination__prev:disabled, & .cn-pagination__next:disabledA button at a bound (first or last page): 50% opacity, no hover; stays visible so the layout never jumps. (disabled (or aria-disabled="true" on links))

                                Rules

                                • At most seven page slots: first, last, the current page with one neighbour on each side, and ellipses for the gaps.
                                • Page items are 36 × 36 (sm 32), 14px medium, 2px apart; the current page is gray-50 with darker text, the same look as hover. Never an inverse fill.
                                • Previous and Next are always present and always the secondary-button look; at a bound they are disabled, never removed, so the layout does not shift. Under 768px they become 36px icon-only buttons.
                                • page-default under page-level tables and lists; minimal or button-group in a table card footer (12 × 24px padding); simple for cursor APIs, unknown totals and drawers.
                                • The minimal summary reads "Page N of M" in 14px medium; the default summary reads "1–10 of 240". Never both.
                                • dots and line are for carousels and onboarding slides only (aria-current="true" on the current one); never for tables.
                                • The page-size selector ("10 per page") belongs to the table toolbar or the advanced card footer, not next to the page items.
                                • sm only inside cards and drawers; md under page-level tables.
                                • When the page number is part of the URL, render every control as a link; otherwise as buttons. Never mix.

                                Accessibility

                                • Root is <nav aria-label="Pagination">; when there are several on a page, name them ("Customers pagination").
                                • The current page carries aria-current="page" and is announced as such; the gray fill is the visual cue, the attribute is the semantic one.
                                • Prev / Next have aria-label="Previous page" / "Next page" even when the word is visible; the arrows are aria-hidden.
                                • Ellipses are aria-hidden; screen readers hear the page numbers, not "dot dot dot".
                                • Disabled bounds use the disabled attribute on buttons or aria-disabled="true" on links (keep the href out of the tab order).
                                • Announce page changes: put the summary in an aria-live="polite" region when the table updates in place.
                                • Dots and lines are buttons with aria-label "Slide N" / "Step N" and aria-current="true" on the active one.
                                Navigation

                                Stepper .cn-stepperedit

                                Progress through a sequence in the reference's four marker styles: numbered circles, step icons (disc with a dot, brand when current, check when complete), minimal dots, or a 4px bar over the text; 24 / 32px, joined by 2px connectors, horizontal or vertical, with a semibold title and supporting text per step.

                                Use: Use above a multi-step form or wizard (onboarding, import, checkout) with 3–6 steps that happen in order, or as a vertical rail beside a long form. Not for switching between peer views (Tabs), not for percentage progress (Progress) and not for a timeline of past events (Timeline).

                                Icon with text (default md)The reference step icon: gray-50 disc with a gray-300 dot; current is brand-600 with a white dot and a 4px halo, complete is brand with a white check. 2px connectors turn brand after a complete step.
                                1. Completed:Your detailsName and email
                                2. Company detailsWebsite and location
                                3. Invite your teamStart collaborating
                                4. Add your socialsShare posts to your accounts
                                HTML
                                <ol class="cn-stepper" data-variant="icon" data-orientation="horizontal" data-size="md" aria-label="Account setup"><li class="cn-stepper__step" data-state="complete"><span class="cn-stepper__indicator" aria-hidden="false"><svg class="cn-stepper__check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-sr-only">Completed:</span></span><span class="cn-stepper__content"><span class="cn-stepper__label">Your details</span><span class="cn-stepper__description">Name and email</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step" data-state="current" aria-current="step"><span class="cn-stepper__indicator" aria-hidden="true"><span class="cn-stepper__dot"></span></span><span class="cn-stepper__content"><span class="cn-stepper__label">Company details</span><span class="cn-stepper__description">Website and location</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step"><span class="cn-stepper__indicator" aria-hidden="true"><span class="cn-stepper__dot"></span></span><span class="cn-stepper__content"><span class="cn-stepper__label">Invite your team</span><span class="cn-stepper__description">Start collaborating</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step"><span class="cn-stepper__indicator" aria-hidden="true"><span class="cn-stepper__dot"></span></span><span class="cn-stepper__content"><span class="cn-stepper__label">Add your socials</span><span class="cn-stepper__description">Share posts to your accounts</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li></ol>
                                Icon with numberNumbers in ringed 32px circles; the current one has a 2px brand ring, the complete one is filled with a check.
                                1. Completed:Your detailsName and email
                                2. Company detailsWebsite and location
                                3. Invite your teamStart collaborating
                                4. Add your socialsShare posts to your accounts
                                HTML
                                <ol class="cn-stepper" data-variant="number" data-orientation="horizontal" data-size="md" aria-label="Account setup"><li class="cn-stepper__step" data-state="complete"><span class="cn-stepper__indicator" aria-hidden="false"><svg class="cn-stepper__check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-sr-only">Completed:</span></span><span class="cn-stepper__content"><span class="cn-stepper__label">Your details</span><span class="cn-stepper__description">Name and email</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step" data-state="current" aria-current="step"><span class="cn-stepper__indicator" aria-hidden="true">2</span><span class="cn-stepper__content"><span class="cn-stepper__label">Company details</span><span class="cn-stepper__description">Website and location</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step"><span class="cn-stepper__indicator" aria-hidden="true">3</span><span class="cn-stepper__content"><span class="cn-stepper__label">Invite your team</span><span class="cn-stepper__description">Start collaborating</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step"><span class="cn-stepper__indicator" aria-hidden="true">4</span><span class="cn-stepper__content"><span class="cn-stepper__label">Add your socials</span><span class="cn-stepper__description">Share posts to your accounts</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li></ol>
                                Minimal dots and connected dots10px (md) or 8px (sm) dots; labels are optional. Complete dots are brand, the current one has the halo.
                                1. Your details
                                2. Company details
                                3. Invite your team
                                4. Add your socials
                                HTML
                                <div style="display:flex;flex-direction:column;gap:var(--cn-space-8);width:100%"><ol class="cn-stepper" data-variant="dot" data-orientation="horizontal" data-size="md" aria-label="Onboarding"><li class="cn-stepper__step" data-state="complete"><span class="cn-stepper__indicator" aria-hidden="false"></span><span class="cn-stepper__content"><span class="cn-stepper__label">Your details</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step" data-state="current" aria-current="step"><span class="cn-stepper__indicator" aria-hidden="true"></span><span class="cn-stepper__content"><span class="cn-stepper__label">Company details</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step"><span class="cn-stepper__indicator" aria-hidden="true"></span><span class="cn-stepper__content"><span class="cn-stepper__label">Invite your team</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step"><span class="cn-stepper__indicator" aria-hidden="true"></span><span class="cn-stepper__content"><span class="cn-stepper__label">Add your socials</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li></ol><div style="max-width:480px"><ol class="cn-stepper" data-variant="dot" data-orientation="horizontal" data-size="sm" aria-label="Onboarding"><li class="cn-stepper__step" data-state="complete"><span class="cn-stepper__indicator" aria-hidden="false"></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step" data-state="complete"><span class="cn-stepper__indicator" aria-hidden="false"></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step" data-state="current" aria-current="step"><span class="cn-stepper__indicator" aria-hidden="true"></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step"><span class="cn-stepper__indicator" aria-hidden="true"></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step"><span class="cn-stepper__indicator" aria-hidden="true"></span><span class="cn-stepper__connector" aria-hidden="true"></span></li></ol></div></div>
                                Text with lineA 4px bar over each step: gray-100 upcoming, brand-600 current and complete; no connectors, steps share the width.
                                1. Your detailsName and email
                                2. Company detailsWebsite and location
                                3. Invite your teamStart collaborating
                                4. Add your socialsShare posts to your accounts
                                HTML
                                <ol class="cn-stepper" data-variant="line" data-orientation="horizontal" data-size="md" aria-label="Account setup"><li class="cn-stepper__step" data-state="complete"><span class="cn-stepper__indicator" aria-hidden="false"></span><span class="cn-stepper__content"><span class="cn-stepper__label">Your details</span><span class="cn-stepper__description">Name and email</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step" data-state="current" aria-current="step"><span class="cn-stepper__indicator" aria-hidden="true"></span><span class="cn-stepper__content"><span class="cn-stepper__label">Company details</span><span class="cn-stepper__description">Website and location</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step"><span class="cn-stepper__indicator" aria-hidden="true"></span><span class="cn-stepper__content"><span class="cn-stepper__label">Invite your team</span><span class="cn-stepper__description">Start collaborating</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step"><span class="cn-stepper__indicator" aria-hidden="true"></span><span class="cn-stepper__content"><span class="cn-stepper__label">Add your socials</span><span class="cn-stepper__description">Share posts to your accounts</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li></ol>
                                Vertical with an errorThe vertical rail (max 480px): the connector runs down from each 32px indicator; the error step turns red and says what to fix.
                                1. Completed:Upload filecustomers-q3.csv · 1,204 rows
                                2. Map columns2 columns are unmapped: “Phone”, “Owner”
                                3. ValidateCheck duplicates and formats
                                4. ImportAdd 1,204 customers to Lumen
                                HTML
                                <ol class="cn-stepper" data-variant="icon" data-orientation="vertical" data-size="md" aria-label="CSV import"><li class="cn-stepper__step" data-state="complete"><span class="cn-stepper__indicator" aria-hidden="false"><svg class="cn-stepper__check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-sr-only">Completed:</span></span><span class="cn-stepper__content"><span class="cn-stepper__label">Upload file</span><span class="cn-stepper__description">customers-q3.csv · 1,204 rows</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step" data-state="error"><span class="cn-stepper__indicator" aria-hidden="false"><span class="cn-stepper__dot"></span></span><span class="cn-stepper__content"><span class="cn-stepper__label">Map columns</span><span class="cn-stepper__description">2 columns are unmapped: “Phone”, “Owner”</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step"><span class="cn-stepper__indicator" aria-hidden="true"><span class="cn-stepper__dot"></span></span><span class="cn-stepper__content"><span class="cn-stepper__label">Validate</span><span class="cn-stepper__description">Check duplicates and formats</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step"><span class="cn-stepper__indicator" aria-hidden="true"><span class="cn-stepper__dot"></span></span><span class="cn-stepper__content"><span class="cn-stepper__label">Import</span><span class="cn-stepper__description">Add 1,204 customers to Lumen</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li></ol>
                                Small, vertical lineIn the vertical line variant the 4px bar runs beside each step.
                                1. DetailsName and audience
                                2. Recipients1,204 customers
                                3. ScheduleThursday, 09:00
                                4. SendReview and confirm
                                HTML
                                <ol class="cn-stepper" data-variant="line" data-orientation="vertical" data-size="sm" aria-label="Send campaign"><li class="cn-stepper__step" data-state="complete"><span class="cn-stepper__indicator" aria-hidden="false"></span><span class="cn-stepper__content"><span class="cn-stepper__label">Details</span><span class="cn-stepper__description">Name and audience</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step" data-state="complete"><span class="cn-stepper__indicator" aria-hidden="false"></span><span class="cn-stepper__content"><span class="cn-stepper__label">Recipients</span><span class="cn-stepper__description">1,204 customers</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step" data-state="current" aria-current="step"><span class="cn-stepper__indicator" aria-hidden="true"></span><span class="cn-stepper__content"><span class="cn-stepper__label">Schedule</span><span class="cn-stepper__description">Thursday, 09:00</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step"><span class="cn-stepper__indicator" aria-hidden="true"></span><span class="cn-stepper__content"><span class="cn-stepper__label">Send</span><span class="cn-stepper__description">Review and confirm</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li></ol>
                                Small numbers, labels onlyInside a dialog or drawer: 24px indicators, no descriptions.
                                1. Completed:Details
                                2. Completed:Recipients
                                3. Schedule
                                4. Send
                                HTML
                                <ol class="cn-stepper" data-variant="number" data-orientation="horizontal" data-size="sm" aria-label="Send campaign"><li class="cn-stepper__step" data-state="complete"><span class="cn-stepper__indicator" aria-hidden="false"><svg class="cn-stepper__check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-sr-only">Completed:</span></span><span class="cn-stepper__content"><span class="cn-stepper__label">Details</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step" data-state="complete"><span class="cn-stepper__indicator" aria-hidden="false"><svg class="cn-stepper__check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-sr-only">Completed:</span></span><span class="cn-stepper__content"><span class="cn-stepper__label">Recipients</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step" data-state="current" aria-current="step"><span class="cn-stepper__indicator" aria-hidden="true">3</span><span class="cn-stepper__content"><span class="cn-stepper__label">Schedule</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step"><span class="cn-stepper__indicator" aria-hidden="true">4</span><span class="cn-stepper__content"><span class="cn-stepper__label">Send</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li></ol>

                                Matrix

                                variant ↓ / size →smmd
                                number
                                1. Completed:Your detailsName and email
                                2. Company detailsWebsite and location
                                3. Invite your teamStart collaborating
                                4. Add your socialsShare posts to your accounts
                                1. Completed:Your detailsName and email
                                2. Company detailsWebsite and location
                                3. Invite your teamStart collaborating
                                4. Add your socialsShare posts to your accounts
                                icon
                                1. Completed:Your detailsName and email
                                2. Company detailsWebsite and location
                                3. Invite your teamStart collaborating
                                4. Add your socialsShare posts to your accounts
                                1. Completed:Your detailsName and email
                                2. Company detailsWebsite and location
                                3. Invite your teamStart collaborating
                                4. Add your socialsShare posts to your accounts
                                dot
                                1. Completed:Your detailsName and email
                                2. Company detailsWebsite and location
                                3. Invite your teamStart collaborating
                                4. Add your socialsShare posts to your accounts
                                1. Completed:Your detailsName and email
                                2. Company detailsWebsite and location
                                3. Invite your teamStart collaborating
                                4. Add your socialsShare posts to your accounts
                                line
                                1. Completed:Your detailsName and email
                                2. Company detailsWebsite and location
                                3. Invite your teamStart collaborating
                                4. Add your socialsShare posts to your accounts
                                1. Completed:Your detailsName and email
                                2. Company detailsWebsite and location
                                3. Invite your teamStart collaborating
                                4. Add your socialsShare posts to your accounts
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-stepper <ol> — Ordered list of steps with aria-label. Flex row (horizontal) or column (vertical), 16px gaps.
                                • .cn-stepper__step <li> — One step: indicator + content + connector. data-state="complete|current|error" (omit for upcoming); aria-current="step" on the current one.
                                • .cn-stepper__indicator <span> — The marker: a 24 / 32px circle (number or icon), an 8 / 10px dot, or a 4px bar. Upcoming: gray-50 with a gray-300 ring or dot; current: brand-600 with a 4px brand-100 halo; complete: brand-600 with a white check; error: red.
                                • .cn-stepper__dot <span> · optional — The inner dot of the icon variant: 8 / 10px, gray-300 when upcoming, white when current.
                                • .cn-stepper__check <svg> · optional — 12 / 16px white check mark inside a complete indicator (number and icon variants).
                                • .cn-stepper__content <span> — Column holding label and description, 2px apart, aligned to the indicator's center (under the bar in the line variant).
                                • .cn-stepper__label <span> — Step title, 14px semibold: 1–3 words ("Company details"). Gray-700 when upcoming or complete, brand-700 when current.
                                • .cn-stepper__description <span> · optional — Optional 14px supporting text under the title, fg-muted: "Website and location". Carries the message in the error state.
                                • .cn-stepper__connector <span> — 2px gray-200 line from this step to the next; fills the gap horizontally or runs down the indicator's axis vertically. Hidden on the last step and in the line variant; brand-600 after a complete step.

                                Props

                                propvalues (default outlined)meaning
                                variantnumber icon dot linenumber = the step number in a ringed circle (forms with many steps). icon = the reference step icon: a gray-50 disc with a gray-300 dot that turns brand with a white dot when current and a check when complete (onboarding, wizards). dot = minimal 8 / 10px dots joined by connectors, labels optional (compact progress under a dialog title). line = a 4px bar above each step's text, no connectors (the reference "text with line").
                                orientationhorizontal verticalhorizontal = a row across the top of a wizard, steps share the width and connectors fill the gaps; up to 5 steps. vertical = a column in a side rail or next to a long form (the reference 400px column); the connector runs down from each indicator, so descriptions can be longer.
                                sizesm mdmd = 32px circles (10px dots, 16px check), 14px titles; the default above a page-level form. sm = 24px circles (8px dots, 12px check), 14px titles; inside dialogs, drawers and cards.

                                States

                                stateselectormeaning
                                complete .cn-stepper__step[data-state="complete"]Done (on the STEP, not the root): indicator filled brand-600 with a white check (bar or dot turns brand); the connector after it turns brand-600; title stays gray-700. (data-state="complete" on the li; put the check svg (and sr-only "Completed:") inside the indicator instead of the number)
                                current .cn-stepper__step[data-state="current"], & .cn-stepper__step[aria-current="step"]The step the user is on: brand-600 indicator with a white dot (number variant: 2px brand ring and brand number) and a 4px brand-100 halo; title in brand-700. Exactly one. (data-state="current" and aria-current="step" on the li)
                                error .cn-stepper__step[data-state="error"]A visited step that failed validation: red-500 ring on a red-50 disc, red title; the description says what to fix. (data-state="error" on the li, with the problem in the description)
                                upcoming .cn-stepper__step:not([data-state])Not reached yet: gray-50 disc with a gray-300 ring or dot, gray-700 title. The default; no attribute needed. (no data-state)

                                Rules

                                • 3–6 steps. Two steps are a single form with a confirm; more than six means the flow needs to be split or some steps merged.
                                • Titles are short and specific to the task. Labeled horizontal steps stack below 768px so titles and descriptions stay readable; unlabeled progress dots remain a row.
                                • Exactly one current step at all times; everything before it is complete (or error), everything after it upcoming.
                                • Complete steps may be clickable (wrap the label in a Link, or make the step a button) to go back; upcoming steps are never clickable.
                                • Descriptions are optional and short (≤ 5 words); in the error state the description is the message that says what to fix.
                                • Error only on a visited step and only with a message; never mark an upcoming step as error.
                                • icon or number for wizards above a form; dot for compact progress under a dialog title; line when the steps are also section titles of a long page.
                                • Horizontal for up to 5 steps above a wizard; vertical (max 480px wide) when there are 5–6 steps, when descriptions are needed, or in a side rail beside a long form.
                                • Indicators are 32px (sm 24) with 2px connectors; the check appears only on complete steps. No custom icons, letters or emoji in the circles.
                                • The stepper shows position, it does not save. Moving between steps is done by the form's Back / Continue buttons.

                                Accessibility

                                • Root is an <ol aria-label="…"> so the order is announced ("step 2 of 4"); each step is an <li>.
                                • The current step carries aria-current="step" on the li in addition to data-state.
                                • Status is not color alone: complete indicators contain the check plus sr-only "Completed:", error indicators keep the number plus sr-only ", needs attention:".
                                • Connectors and decorative indicators are aria-hidden; the label text is the accessible name of the step. Dot steps without labels need an sr-only label each.
                                • If steps are clickable, they are links or buttons with the step name as text, and upcoming steps are not focusable.
                                Navigation

                                HeaderNavigation .cn-header-navigationedit

                                Marketing website header: 80px bar with logo, text links (with optional mega menu), and Log in / Sign up actions; collapses to a hamburger on mobile. "header navigation".

                                Use: Top of every marketing page (landing, pricing, about, blog). Inside the product use Topbar or Sidebar instead.

                                DefaultComponent preview
                                1280 × 384
                                HTML
                                <header class="cn-header-navigation" data-variant="default" data-theme="light"><div class="cn-header-navigation__container"><a href="#" class="cn-header-navigation__logo"><span class="cn-header-navigation__mark" aria-hidden="true"></span>Lumen</a><nav class="cn-header-navigation__nav" aria-label="Main"><a href="#" class="cn-header-navigation__item" aria-current="page">Home</a><a href="#" class="cn-header-navigation__item">Products<svg class="cn-header-navigation__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></a><a href="#" class="cn-header-navigation__item">Resources<svg class="cn-header-navigation__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></a><a href="#" class="cn-header-navigation__item">Pricing</a></nav><div class="cn-header-navigation__actions"><button type="button" class="cn-button" data-variant="ghost" data-size="lg"><span class="cn-button__label">Log in</span></button><button type="button" class="cn-button" data-variant="primary" data-size="lg"><span class="cn-button__label">Sign up</span></button></div><button type="button" class="cn-header-navigation__toggle" aria-label="Open menu"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M3 5h10M3 8h10M3 11h10"/></svg></button></div></header>
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-header-navigation <header> — Full-width bar, 80px, bottom hairline (transparent when over a hero).
                                • .cn-header-navigation__container <div> — Centered 1280px row with 32px side padding.
                                • .cn-header-navigation__logo <a> — Logo mark + wordmark, links home.
                                • .cn-header-navigation__mark <span> — 32px mark.
                                • .cn-header-navigation__nav <nav> — The text links, 8px apart.
                                • .cn-header-navigation__item <a> — 16px semibold link, 40px tall, fg-muted → fg-default; current is fg-default.
                                • .cn-header-navigation__chevron <svg> — Dropdown indicator, 20px.
                                • .cn-header-navigation__actions <div> — Right side: Log in (ghost) and Sign up (primary), lg size.
                                • .cn-header-navigation__toggle <button> — Hamburger IconButton shown under 1024px.
                                • .cn-header-navigation__menu <div> · optional — Mega menu panel: white, radius 12, shadow-lg, 2–3 columns of menu items.
                                • .cn-header-navigation__menu-item <a> — Icon + title + description row, 12px radius, hover bg-subtle.
                                • .cn-header-navigation__menu-icon <svg> — 24px icon in brand color.
                                • .cn-header-navigation__menu-text <span> — Title and description column.
                                • .cn-header-navigation__menu-title <span> — label-md semibold.
                                • .cn-header-navigation__menu-desc <span> — body-md fg-muted.

                                Props

                                propvalues (default outlined)meaning
                                variantdefault transparent with-menudefault = white with bottom hairline; transparent = no background/border (over a hero); with-menu = shows the mega menu panel open under the bar (static preview).
                                themelight darkdark = gray-950 bar with light text (dark landing pages).

                                States

                                stateselectormeaning
                                hover .cn-header-navigation__item:hoverLink darkens. (native)
                                current .cn-header-navigation__item[aria-current="page"]Current section is fg-default. (aria-current="page")
                                open[data-state="open"]Mega menu visible. (data-state="open" on the root)

                                Rules

                                • 80px tall on desktop, 64 on mobile; 1280px container with 32px padding; logo left, links after a 40px gap, actions right.
                                • Links: 16px semibold gray; ≤ 5 items; dropdown items show a chevron and open a mega menu with icon + title + one-line description rows.
                                • Actions: exactly "Log in" (tertiary/ghost, lg) and one primary CTA (lg). Never two filled buttons.
                                • Below 1024px, links and actions hide behind the hamburger; the mobile menu lists the same links stacked, then the two actions full width.
                                • Transparent variant only over a hero whose background guarantees contrast.

                                Accessibility

                                • <header> + <nav aria-label="Main">; dropdown triggers use aria-expanded; the mega menu is reachable by keyboard and closes with Escape.
                                • The hamburger is a labelled button.
                                Navigation

                                MobileHeader .cn-mobile-headeredit

                                The 64px top bar that replaces the Sidebar on small screens: logo on the left, a 40px hamburger on the right, a bottom hairline; plus the 296px drawer it opens with a logo row, 48px navigation items, the support and settings group and the account card at the bottom. The drawer is positioned under the bar (absolute), never fixed.

                                Use: Use as the app header below the lg breakpoint, paired with the Sidebar above it. Not for marketing sites (HeaderNavigation has its own mobile menu) and not as a page header inside content (PageHeader).

                                SimpleComponent previewThe resting bar: logo left, hamburger right, hairline below. The drawer is in the DOM but hidden.
                                HTML
                                <div style="width:100%;max-width:375px"><header class="cn-mobile-header" data-variant="simple"><a href="#" class="cn-mobile-header__logo" aria-label="Lumen home"><span class="cn-mobile-header__mark" aria-hidden="true"></span><span class="cn-mobile-header__brand">Lumen</span></a><button type="button" class="cn-mobile-header__toggle" aria-label="Open menu" aria-expanded="false" aria-controls="mh-drawer"><svg class="cn-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"/></svg></button></header></div>
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-mobile-header <header> — The 64px bar: flex, space-between, 16px horizontal padding, bg-surface, bottom hairline; relative so the drawer anchors to it.
                                • .cn-mobile-header__logo <a> — Link to home holding the mark and the wordmark. aria-label "Lumen home".
                                • .cn-mobile-header__mark <span> — 28px action-colored square mark (replace with the real logo).
                                • .cn-mobile-header__brand <span> — The product name, label-md semibold.
                                • .cn-mobile-header__actions <div> · optional — Optional cluster before the toggle (search, notifications) as ghost IconButtons.
                                • .cn-mobile-header__toggle <button> — 40px hamburger button with a 24px icon; aria-expanded and aria-controls point at the drawer; shows an × when open.
                                • .cn-mobile-header__scrim <div> · optional — The overlay under the bar that dims the page while the drawer is open (bg-overlay). Hidden when closed.
                                • .cn-mobile-header__drawer <nav> · optional — The 296px panel under the bar: bg-surface, right hairline, shadow-xl, 16px padding, flex column. aria-label "Main". Hidden when closed.
                                • .cn-mobile-header__drawer-header <div> · optional — First row of the drawer: the logo again and a CloseButton.
                                • .cn-mobile-header__nav <div> · optional — Main list of nav items, 4px apart, fills the drawer.
                                • .cn-mobile-header__item <a> · optional — 48px navigation link: 24px icon, 16px semibold label, optional count. aria-current="page" on the current one.
                                • .cn-mobile-header__icon <svg> · optional — 24px icon in fg-subtle before the label.
                                • .cn-mobile-header__label <span> · optional — The item text, truncated.
                                • .cn-mobile-header__badge <span> · optional — Trailing count pill (22px, bg-subtle, body-xs).
                                • .cn-mobile-header__footer <div> · optional — Bottom group: Support and Settings items above the account card; top hairline.
                                • .cn-mobile-header__account <div> · optional — Account card: 40px avatar, name + email, log-out button, in a hairline card.
                                • .cn-mobile-header__avatar <span> · optional — 40px circle with initials; role="img" + aria-label.
                                • .cn-mobile-header__account-text <div> · optional — Name and email column.
                                • .cn-mobile-header__name <span> · optional — label-sm, truncated.
                                • .cn-mobile-header__email <span> · optional — body-sm muted, truncated.
                                • .cn-mobile-header__account-action <button> · optional — 36px utility button (log out). aria-label required.

                                Props

                                propvalues (default outlined)meaning
                                variantsimple with-drawer-opensimple = the bar only; the drawer and scrim are hidden (the resting state). with-drawer-open = the drawer and scrim are shown under the bar and the toggle shows ×; the app switches to this state when the toggle is pressed.

                                States

                                stateselectormeaning
                                expanded .cn-mobile-header__toggle[aria-expanded="true"]The toggle while the drawer is open (on the toggle; extraCss): subtle fill and ink icon. (aria-expanded="true" on .cn-mobile-header__toggle)
                                current .cn-mobile-header__item[aria-current="page"]The current destination (on the item; extraCss): subtle fill, ink text. (aria-current="page" on .cn-mobile-header__item)
                                hover .cn-mobile-header__item:hover, & .cn-mobile-header__toggle:hoverPointer over an item or the toggle (extraCss): subtle fill. (native :hover)
                                focus .cn-mobile-header__item:focus-visible, & .cn-mobile-header__toggle:focus-visible, & .cn-mobile-header__logo:focus-visibleKeyboard focus shows the ring on links and buttons (extraCss). (native :focus-visible)

                                Rules

                                • Show the MobileHeader below the lg breakpoint and the Sidebar above it; never both at once.
                                • The bar is 64px with the logo on the left and the hamburger on the right; at most two icon actions between them.
                                • The drawer mirrors the Sidebar exactly: same items, same order, same account card. Do not add mobile-only destinations.
                                • Items are 48px so they are comfortable to tap; the current one has the subtle fill and aria-current="page".
                                • Opening the drawer swaps the hamburger for an ×, adds the scrim and locks page scroll; closing restores focus to the toggle.
                                • Counts use the badge part; never red dots for ordinary counts.
                                • In the app, position the drawer as a fixed layer in the app shell; the component itself only styles the panel under the bar.
                                • Escape and a tap on the scrim close the drawer; swiping left may close it but is never the only way.

                                Accessibility

                                • The toggle is a <button> with aria-label ("Open menu" / "Close menu"), aria-expanded and aria-controls pointing at the drawer id.
                                • The drawer is a <nav aria-label="Main">; the current item carries aria-current="page". While open, focus moves into the drawer and stays there until it closes.
                                • The scrim is aria-hidden; the CloseButton inside the drawer duplicates the toggle for screen-reader users who are inside the panel.
                                • Initials avatars carry role="img" with the full name; the log-out button has an aria-label.
                                • Touch targets are at least 40px (toggle) and 48px (items); the logo link has an aria-label because the mark alone has no name.
                                Navigation

                                Sidebar .cn-sidebaredit

                                The application sidebar family of the system: a 280px column with logo, search, navigation items matching SidebarNav (36px, label-md, 20px icons), a bottom group and an account card; slim 68px rail; dual-tier rail + panel; sections with subheadings; dark theme.

                                Use: Primary navigation of an application with more than 5 destinations. For 3–5 destinations prefer the Topbar with tabs. Groups: main destinations at top, support/settings at the bottom, account card last. Never one item per database table; items are user tasks.

                                SimpleComponent preview
                                HTML
                                <nav class="cn-sidebar" data-variant="simple" data-theme="light" aria-label="Main"><div class="cn-sidebar__brand"><span class="cn-sidebar__logo" aria-hidden="true"></span><span class="cn-sidebar__brand-name">Lumen</span></div><div class="cn-sidebar__search cn-input" data-variant="default" data-size="md"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search" aria-label="Search"></div><div class="cn-sidebar__nav"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg><span class="cn-sidebar__label">Home</span></a><a href="#" class="cn-sidebar__item" aria-current="page"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><span class="cn-sidebar__label">Dashboard</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-sidebar__label">Projects</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-sidebar__label">Tasks</span><span class="cn-sidebar__badge">10</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-sidebar__label">Reporting</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><span class="cn-sidebar__label">Users</span></a></div><div class="cn-sidebar__footer"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg><span class="cn-sidebar__label">Support</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg><span class="cn-sidebar__label">Settings</span></a><div class="cn-sidebar__account"><span class="cn-sidebar__avatar" aria-hidden="true">MC</span><div class="cn-sidebar__account-text"><span class="cn-sidebar__name">Maya Chen</span><span class="cn-sidebar__email">maya@lumen.co</span></div><button type="button" class="cn-sidebar__account-action" aria-label="Log out"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button></div></div></nav>
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-sidebar <nav> — The column: flex column, full height, right hairline.
                                • .cn-sidebar__brand <div> — Logo row at the top (32px logo + name).
                                • .cn-sidebar__logo <span> — 32px brand mark.
                                • .cn-sidebar__brand-name <span> — Product name, label-lg semibold (hidden in slim).
                                • .cn-sidebar__search <div> · optional — Optional search Input under the logo.
                                • .cn-sidebar__nav <div> — Main list of items (flex column, gap 2px).
                                • .cn-sidebar__section <div> · optional — A group with a subheading (sections variant).
                                • .cn-sidebar__heading <div> · optional — Group subheading: label-xs in fg-subtle, 8px horizontal padding.
                                • .cn-sidebar__item <a> — Nav item: 36px, 8px horizontal padding and gap, radius 6, label-md (14px medium by default), fg-muted; icon + label + optional badge.
                                • .cn-sidebar__icon <svg> — 20px icon in fg-subtle (fg-muted when current).
                                • .cn-sidebar__label <span> — The item text, truncated.
                                • .cn-sidebar__badge <span> · optional — Trailing count pill (22px, bg-subtle, body-xs).
                                • .cn-sidebar__divider <hr> · optional — Hairline between groups (dividers variant).
                                • .cn-sidebar__footer <div> — Bottom group: support/settings items then the account card.
                                • .cn-sidebar__account <div> — Account card: avatar 40, name + email, trailing log-out icon; bordered card in the default theme.
                                • .cn-sidebar__avatar <span> — 40px circle with initials or image.
                                • .cn-sidebar__account-text <div> — Name and email column.
                                • .cn-sidebar__name <span> — label-sm semibold.
                                • .cn-sidebar__email <span> — body-sm fg-muted, truncated.
                                • .cn-sidebar__account-action <button> — 36px utility button (log out / ⋯).
                                • .cn-sidebar__rail <div> · optional — The 68px icon-only column of the dual-tier variant.
                                • .cn-sidebar__panel <div> · optional — The 256px second-tier panel of the dual-tier variant.
                                • .cn-sidebar__rail-item <a> · optional — 48px square icon button with a 20px icon in a rail.

                                Props

                                propvalues (default outlined)meaning
                                variantsimple slim dual-tier sections dividerssimple = one flat list (280px); slim = 68px icon rail with tooltips; dual-tier = 68px rail + 256px panel for the active area; sections = groups with subheadings; dividers = groups separated by hairlines.
                                themelight dark brandlight = white; dark = gray-950 surface with gray-800 borders; brand = brand-700 surface (marketing-style app).

                                States

                                stateselectormeaning
                                current .cn-sidebar__item[aria-current="page"]The active destination: subtle fill with action-colored text and icon. (aria-current="page" on the item)
                                hover .cn-sidebar__item:hoverSubtle fill. (native)
                                focus .cn-sidebar__item:focus-visibleFocus ring. (native)

                                Rules

                                • Width 280px, matching SidebarNav. Slim rail 68px. Dual tier 68 + 256. The sidebar never scrolls the page; its own nav scrolls.
                                • Items match SidebarNav: 36px tall with a 20px icon, 8px gap and horizontal padding, label-md typography, and 2px between rows. The current item uses action-colored text and a subtle surface; hover uses a quiet neutral fill.
                                • Order: logo, optional search, main items (≤ 7), sections, then the footer with Support/Settings and the account card.
                                • Counts use the badge part; never red dots for ordinary counts.
                                • On tablets collapse to the slim rail; on mobile replace with the MobileHeader drawer.

                                Accessibility

                                • <nav aria-label="Main">; the current item carries aria-current="page".
                                • Slim rail items are icon-only and need aria-label (and a Tooltip on hover).
                                • The account log-out control is a labelled button.
                                Data display

                                Badge .cn-badgeedit

                                Small status label with compact corners (or an optional pill): 12/14px medium text, a 1px ring one step darker than the fill, optional dot, icon, avatar or close button. The tone carries meaning, the variant carries emphasis.

                                Use: Statuses, categories and counts next to a title, in a table cell, inside a tab or a nav item. Not for actions (use Button) and not for selectable or removable filters (use Tag) — the close button here only dismisses a label.

                                Colors (pill, soft)Order: gray, brand, error, warning, success, then the decorative hues.
                                Label Label Label Label Label Label Label Label Label Label Label Label Label
                                HTML
                                <span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="md" data-shape="square">Label</span> <span class="cn-badge" data-tone="accent" data-variant="soft" data-size="md" data-shape="square">Label</span> <span class="cn-badge" data-tone="danger" data-variant="soft" data-size="md" data-shape="square">Label</span> <span class="cn-badge" data-tone="warning" data-variant="soft" data-size="md" data-shape="square">Label</span> <span class="cn-badge" data-tone="success" data-variant="soft" data-size="md" data-shape="square">Label</span> <span class="cn-badge" data-tone="info" data-variant="soft" data-size="md" data-shape="square">Label</span> <span class="cn-badge" data-tone="gray-blue" data-variant="soft" data-size="md" data-shape="square">Label</span> <span class="cn-badge" data-tone="blue-light" data-variant="soft" data-size="md" data-shape="square">Label</span> <span class="cn-badge" data-tone="blue" data-variant="soft" data-size="md" data-shape="square">Label</span> <span class="cn-badge" data-tone="indigo" data-variant="soft" data-size="md" data-shape="square">Label</span> <span class="cn-badge" data-tone="purple" data-variant="soft" data-size="md" data-shape="square">Label</span> <span class="cn-badge" data-tone="pink" data-variant="soft" data-size="md" data-shape="square">Label</span> <span class="cn-badge" data-tone="orange" data-variant="soft" data-size="md" data-shape="square">Label</span>
                                Sizessm 22px · md 24px · lg 28px.
                                Label Label Label
                                HTML
                                <span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm" data-shape="square">Label</span> <span class="cn-badge" data-tone="accent" data-variant="soft" data-size="md" data-shape="square">Label</span> <span class="cn-badge" data-tone="accent" data-variant="soft" data-size="lg" data-shape="square">Label</span>
                                Types: pill-color, badge-color, badge-modernsquare = 6px radius (8px at lg); modern = white with a gray ring and shadow-xs.
                                Label Label Label Label
                                HTML
                                <span class="cn-badge" data-tone="success" data-variant="soft" data-size="md" data-shape="pill">Label</span> <span class="cn-badge" data-tone="success" data-variant="soft" data-size="md" data-shape="square">Label</span> <span class="cn-badge" data-tone="success" data-variant="modern" data-size="md" data-shape="square">Label</span> <span class="cn-badge" data-tone="success" data-variant="modern" data-size="lg" data-shape="square">Label</span>
                                With dotIn the modern type only the dot takes the color.
                                Active Processing Online Offline
                                HTML
                                <span class="cn-badge" data-tone="success" data-variant="soft" data-size="md" data-shape="pill"><span class="cn-badge__dot"></span>Active</span> <span class="cn-badge" data-tone="accent" data-variant="soft" data-size="md" data-shape="square"><span class="cn-badge__dot"></span>Processing</span> <span class="cn-badge" data-tone="success" data-variant="modern" data-size="md" data-shape="square"><span class="cn-badge__dot"></span>Online</span> <span class="cn-badge" data-tone="neutral" data-variant="modern" data-size="sm" data-shape="square"><span class="cn-badge__dot"></span>Offline</span>
                                With iconLeading or trailing, 12px, addon color.
                                Verified Needs attention Open
                                HTML
                                <span class="cn-badge" data-tone="success" data-variant="soft" data-size="md" data-shape="pill"><svg class="cn-badge__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg>Verified</span> <span class="cn-badge" data-tone="warning" data-variant="soft" data-size="md" data-shape="square"><svg class="cn-badge__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 2.5l6 11H2l6-11zM8 7v3M8 11.5v.5"/></svg>Needs attention</span> <span class="cn-badge" data-tone="accent" data-variant="modern" data-size="md" data-shape="square">Open<svg class="cn-badge__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg></span>
                                With avatar
                                Maya Chen Daniel Costa Sofia Almeida
                                HTML
                                <span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="md" data-shape="pill"><img class="cn-badge__avatar" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="" data-name="Maya Chen">Maya Chen</span> <span class="cn-badge" data-tone="neutral" data-variant="modern" data-size="md" data-shape="square"><img class="cn-badge__avatar" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="" data-name="Daniel Costa">Daniel Costa</span> <span class="cn-badge" data-tone="accent" data-variant="soft" data-size="lg" data-shape="pill"><img class="cn-badge__avatar" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="" data-name="Sofia Almeida">Sofia Almeida</span>
                                With x
                                Design Reporting the system
                                HTML
                                <span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="md" data-shape="pill">Design<button type="button" class="cn-badge__x" aria-label="Remove Design"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span> <span class="cn-badge" data-tone="accent" data-variant="soft" data-size="md" data-shape="square">Reporting<button type="button" class="cn-badge__x" aria-label="Remove Reporting"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span> <span class="cn-badge" data-tone="neutral" data-variant="modern" data-size="lg" data-shape="square">the system<button type="button" class="cn-badge__x" aria-label="Remove the system"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span>
                                Outline and solid
                                Draft Overdue Live New
                                HTML
                                <span class="cn-badge" data-tone="neutral" data-variant="outline" data-size="md" data-shape="square">Draft</span> <span class="cn-badge" data-tone="danger" data-variant="solid" data-size="md" data-shape="square">Overdue</span> <span class="cn-badge" data-tone="success" data-variant="solid" data-size="sm" data-shape="pill"><span class="cn-badge__dot"></span>Live</span> <span class="cn-badge" data-tone="accent" data-variant="outline" data-size="md" data-shape="square">New</span>

                                Matrix

                                variant ↓ / size →smmdlg
                                softLabel Label Label Label Label Label Label Label Label Label Label Label LabelLabel Label Label Label Label Label Label Label Label Label Label Label LabelLabel Label Label Label Label Label Label Label Label Label Label Label Label
                                outlineLabel Label Label Label Label Label Label Label Label Label Label Label LabelLabel Label Label Label Label Label Label Label Label Label Label Label LabelLabel Label Label Label Label Label Label Label Label Label Label Label Label
                                solidLabel Label Label Label Label Label Label Label Label Label Label Label LabelLabel Label Label Label Label Label Label Label Label Label Label Label LabelLabel Label Label Label Label Label Label Label Label Label Label Label Label
                                modernLabel Label Label Label Label Label Label Label Label Label Label Label LabelLabel Label Label Label Label Label Label Label Label Label Label Label LabelLabel Label Label Label Label Label Label Label Label Label Label Label Label
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-badge <span> — The pill (or square). Sizes sm 22 / md 24 / lg 28px tall; text is medium weight, never uppercase.
                                • .cn-badge__dot <span> · optional — Optional 6px status dot before the text in the tone's addon color. Use for live states (online, running).
                                • .cn-badge__icon <svg> · optional — Optional 12px icon before or after the text, in the tone's addon color. Decorative.
                                • .cn-badge__avatar <img> · optional — Optional 16px round image (a person, a flag, a logo) before the text.
                                • .cn-badge__x <button> · optional — Optional 16px close button after the text (12px × icon). Needs aria-label "Remove {text}".

                                Props

                                propvalues (default outlined)meaning
                                toneneutral accent success warning danger info gray-blue blue-light blue indigo purple pink orangeMeaning. neutral (gray) for categories; accent (brand) for brand/AI; success/warning/danger (error) for state; info = blue. The extra hues (gray-blue, blue-light, blue, indigo, purple, pink, orange) are the system's decorative palette for labels and tags: use them only for user-chosen categories, never for state.
                                variantsoft outline solid modernsoft = tinted fill with a ring one step darker (the system pill-color / color); modern = white fill, gray ring and shadow-xs, only the dot/icon takes the tone color; outline = ring only, transparent fill; solid = filled, for one emphasized status per row at most.
                                shapepill squaresquare = compact token-defined corners (default); pill = fully rounded, for an explicitly round visual family.
                                sizesm md lgsm 22px tall with 12px text (tables, tabs, nav items); md 24px with 14px text (default); lg 28px with 14px text (page headers, cards).

                                Rules

                                • Text is 1–2 words in sentence case, medium weight, never uppercase.
                                • One solid badge per row at most. Everything else soft, modern or outline.
                                • Tone is meaning, not decoration: success = done/healthy, warning = needs attention, danger = failed/blocked, info = new/informational, accent = brand or AI-generated, neutral = category. The decorative hues are for user-defined labels only.
                                • Modern badges keep gray text whatever the tone; only the dot or icon takes the color. Use them on tinted backgrounds where a colored fill would clash.
                                • sm inside tables, tabs and nav items; md next to titles; lg in page headers and cards. Match the size of the text next to it.
                                • Never make the badge itself clickable. If it filters, it is a Tag; if it acts, it is a Button. The × only dismisses a label.

                                Accessibility

                                • The text must carry the meaning; the dot, icon and tone are reinforcement. "Working" not just a green dot.
                                • The close button needs aria-label "Remove {text}" and is the only focusable thing in a badge.
                                • Live states with a dot may also use aria-live on the containing region, not on the badge.
                                • Avatar images inside badges are decorative (alt="") because the text names the person.
                                Data display

                                Tag .cn-tagedit

                                The reference tag: a white chip with a gray-300 ring, radius 6 and 14px medium text (sm 24 · md 28 · lg 32px) that you can toggle or remove; leading checkbox, avatar or dot, trailing count chip or close ×. Unlike Badge it responds to focus and selection.

                                Use: Filter bars (toggle or checkbox tags), token fields and applied-filter rows (removable tags), multi-select values, keyword lists with counts. Not for statuses or categories that only inform (Badge), and not for actions with a verb (Button).

                                Sizessm 24px (12px text) · md 28px · lg 32px (14px text). White, gray-300 ring, radius 6.
                                DesignDesignDesign
                                HTML
                                <div style="display:flex;align-items:center;gap:var(--cn-space-3)"><span class="cn-tag" data-tone="neutral" data-variant="outline" data-size="sm"><span class="cn-tag__label">Design</span></span><span class="cn-tag" data-tone="neutral" data-variant="outline" data-size="md"><span class="cn-tag__label">Design</span></span><span class="cn-tag" data-tone="neutral" data-variant="outline" data-size="lg"><span class="cn-tag__label">Design</span></span></div>
                                Removable valuesThe close × is a 16px (14 / 20) radius-3 button with its own focus ring; gray-50 on hover.
                                DesignProductMarketingEngineeringSales
                                HTML
                                <div style="display:flex;flex-wrap:wrap;gap:var(--cn-space-2)"><span class="cn-tag" data-tone="neutral" data-variant="outline" data-size="md"><span class="cn-tag__label">Design</span><button type="button" class="cn-tag__remove" aria-label="Remove Design"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-tag" data-tone="neutral" data-variant="outline" data-size="md"><span class="cn-tag__label">Product</span><button type="button" class="cn-tag__remove" aria-label="Remove Product"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-tag" data-tone="neutral" data-variant="outline" data-size="md"><span class="cn-tag__label">Marketing</span><button type="button" class="cn-tag__remove" aria-label="Remove Marketing"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-tag" data-tone="neutral" data-variant="outline" data-size="sm"><span class="cn-tag__label">Engineering</span><button type="button" class="cn-tag__remove" aria-label="Remove Engineering"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-tag" data-tone="neutral" data-variant="outline" data-size="lg"><span class="cn-tag__label">Sales</span><button type="button" class="cn-tag__remove" aria-label="Remove Sales"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span></div>
                                With countThe count chip: radius 3, gray-100, 12px medium (14px at lg).
                                Design5Design5Design5
                                HTML
                                <div style="display:flex;flex-wrap:wrap;gap:var(--cn-space-2)"><span class="cn-tag" data-tone="neutral" data-variant="outline" data-size="sm"><span class="cn-tag__label">Design</span><span class="cn-tag__count">5</span></span><span class="cn-tag" data-tone="neutral" data-variant="outline" data-size="md"><span class="cn-tag__label">Design</span><span class="cn-tag__count">5</span></span><span class="cn-tag" data-tone="neutral" data-variant="outline" data-size="lg"><span class="cn-tag__label">Design</span><span class="cn-tag__count">5</span></span></div>
                                Checkbox tagsA leading 16px checkbox (14 / 18 at sm / lg); checked = brand-600 with the white check. The tag stays white.
                                HTML
                                <div role="group" aria-label="Teams" style="display:flex;flex-wrap:wrap;gap:var(--cn-space-2)"><button type="button" class="cn-tag" data-tone="neutral" data-variant="outline" data-size="md" role="checkbox" aria-checked="true"><span class="cn-tag__checkbox" aria-hidden="true"><svg class="cn-tag__check" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.67 3.5L5.25 9.92 2.33 7"/></svg></span><span class="cn-tag__label">Design</span></button><button type="button" class="cn-tag" data-tone="neutral" data-variant="outline" data-size="md" role="checkbox" aria-checked="false"><span class="cn-tag__checkbox" aria-hidden="true"><svg class="cn-tag__check" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.67 3.5L5.25 9.92 2.33 7"/></svg></span><span class="cn-tag__label">Product</span></button><button type="button" class="cn-tag" data-tone="neutral" data-variant="outline" data-size="md" role="checkbox" aria-checked="true"><span class="cn-tag__checkbox" aria-hidden="true"><svg class="cn-tag__check" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.67 3.5L5.25 9.92 2.33 7"/></svg></span><span class="cn-tag__label">Engineering</span></button><button type="button" class="cn-tag" data-tone="neutral" data-variant="outline" data-size="md" role="checkbox" aria-checked="false" disabled><span class="cn-tag__checkbox" aria-hidden="true"><svg class="cn-tag__check" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.67 3.5L5.25 9.92 2.33 7"/></svg></span><span class="cn-tag__label">Sales</span></button></div>
                                With avatar and with dotA 16px avatar with the contrast ring; an 8px green dot for live states.
                                Maya ChenDaniel CostaOnlineAvailable
                                HTML
                                <div style="display:flex;flex-wrap:wrap;gap:var(--cn-space-2)"><span class="cn-tag" data-tone="neutral" data-variant="outline" data-size="md"><img class="cn-tag__avatar" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""><span class="cn-tag__label">Maya Chen</span><button type="button" class="cn-tag__remove" aria-label="Remove Maya Chen"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-tag" data-tone="neutral" data-variant="outline" data-size="lg"><img class="cn-tag__avatar" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""><span class="cn-tag__label">Daniel Costa</span><button type="button" class="cn-tag__remove" aria-label="Remove Daniel Costa"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-tag" data-tone="neutral" data-variant="outline" data-size="md"><span class="cn-tag__dot"></span><span class="cn-tag__label">Online</span></span><span class="cn-tag" data-tone="neutral" data-variant="outline" data-size="sm"><span class="cn-tag__dot"></span><span class="cn-tag__label">Available</span></span></div>
                                Selectable filter chipsaria-pressed="true" is the selected state: brand tint, brand ring and text.
                                HTML
                                <div style="display:flex;flex-wrap:wrap;gap:var(--cn-space-2)"><button type="button" class="cn-tag" data-tone="neutral" data-variant="outline" data-size="md" aria-pressed="true"><span class="cn-tag__label">All</span></button><button type="button" class="cn-tag" data-tone="neutral" data-variant="outline" data-size="md" aria-pressed="false"><span class="cn-tag__label">Active</span></button><button type="button" class="cn-tag" data-tone="neutral" data-variant="outline" data-size="md" aria-pressed="false"><span class="cn-tag__label">Archived</span></button><button type="button" class="cn-tag" data-tone="neutral" data-variant="outline" data-size="md" aria-pressed="false"><span class="cn-tag__label">Shared with me</span></button></div>
                                Soft and accentsoft = gray-50 fill, no ring; accent for assistant suggestions and brand-linked values.
                                DraftSuggested: Q3 launchLumen
                                HTML
                                <div style="display:flex;flex-wrap:wrap;gap:var(--cn-space-2)"><span class="cn-tag" data-tone="neutral" data-variant="soft" data-size="md"><span class="cn-tag__label">Draft</span><button type="button" class="cn-tag__remove" aria-label="Remove Draft"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-tag" data-tone="accent" data-variant="soft" data-size="md"><svg class="cn-tag__icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 1.5l1.6 4.9 4.9 1.6-4.9 1.6L8 14.5l-1.6-4.9L1.5 8l4.9-1.6z"/></svg><span class="cn-tag__label">Suggested: Q3 launch</span><button type="button" class="cn-tag__remove" aria-label="Remove Suggested: Q3 launch"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span><span class="cn-tag" data-tone="accent" data-variant="outline" data-size="md"><span class="cn-tag__label">Lumen</span></span></div>
                                Disabled50% opacity, cursor not-allowed.
                                Locked filter
                                HTML
                                <div style="display:flex;flex-wrap:wrap;gap:var(--cn-space-2)"><button type="button" class="cn-tag" data-tone="neutral" data-variant="outline" data-size="md" aria-pressed="false" disabled><span class="cn-tag__label">Archived</span></button><span class="cn-tag" data-tone="neutral" data-variant="outline" data-size="md" aria-disabled="true"><span class="cn-tag__label">Locked filter</span><button type="button" class="cn-tag__remove" aria-label="Remove Locked filter" disabled><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></span></div>

                                Matrix

                                variant ↓ / size →smmdlg
                                outline
                                DesignDesignDesign
                                DesignDesignDesign
                                DesignDesignDesign
                                soft
                                DesignDesignDesign
                                DesignDesignDesign
                                DesignDesignDesign
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-tag <span> — A <span> for a value; a <button aria-pressed> (or role="checkbox" aria-checked with the checkbox part) when selectable. White, gray-300 ring, radius 6.
                                • .cn-tag__checkbox <span> · optional — Leading 14 / 16 / 18px checkbox (radius 4, gray-300 ring); brand-600 with a white check when the tag is checked.
                                • .cn-tag__check <svg> · optional — The 10 / 12 / 14px check inside the checkbox; hidden until checked.
                                • .cn-tag__avatar <img> · optional — Leading 16px round avatar with the 0.5px contrast ring.
                                • .cn-tag__dot <span> · optional — Leading 8px status dot, green-500 by default (override the color inline for other states).
                                • .cn-tag__icon <svg> · optional — Optional 12px leading icon (a flag, a spark for suggested values). Decorative.
                                • .cn-tag__label <span> — The value text, sentence case, 1–3 words. Never truncated.
                                • .cn-tag__count <span> · optional — Trailing count chip: radius 3, gray-100, 12px medium (14px at lg), padding 0 × 4–6.
                                • .cn-tag__remove <button> · optional — Trailing close ×: radius 3, gray-400, 14 / 16 / 20px box with a 10 / 12 / 14px icon; gray-50 on hover. Its own focus target with aria-label "Remove {label}".

                                Props

                                propvalues (default outlined)meaning
                                toneneutral accentneutral = user-chosen values and filters (the reference); accent = values suggested by the assistant or tied to the brand (use sparingly, a few per view).
                                variantoutline softoutline = white with the gray-300 ring (the reference tag, on surfaces); soft = gray-50 fill with no ring (on the canvas, inside inputs, in dense rows).
                                sizesm md lgsm 24px with 12px text (table cells, dense filter rows); md 28px with 14px text (default); lg 32px with 14px text (filter bars next to 32px controls).

                                States

                                stateselectormeaning
                                hover:hover:not(:disabled):not([aria-disabled="true"])Pointer over the element (mouse only; never the only cue). (native :hover)
                                selected[data-selected], &[aria-pressed="true"], &[aria-checked="true"]Toggled on. A pressed chip gets the brand tint; a checkbox tag fills its checkbox brand-600 with the white check. (aria-pressed="true" on a button tag, aria-checked="true" on a role="checkbox" tag, or data-selected)
                                focus:focus-visibleKeyboard focus. Shows the 3px ring; never on mouse click. (native :focus-visible)
                                disabled:disabled, &[aria-disabled="true"]Not interactive. Reduced opacity, no hover, cursor not-allowed. (disabled attribute (or aria-disabled="true"))

                                Rules

                                • Tag is interactive or removable; Badge is not. If it cannot be toggled or removed, it is a Badge.
                                • Labels are nouns in sentence case, 1–3 words, never uppercase and never truncated.
                                • Heights come only from size: 24 / 28 / 32px with 8 / 9 / 10px side padding; the leading and trailing parts pull in by their margins, never by extra padding.
                                • Selectable tags are <button aria-pressed> (brand tint) or role="checkbox" with the checkbox part (white tag, brand checkbox); removable tags are a <span> with a remove <button> inside. A tag is never both selectable and removable.
                                • The remove button is always last, always has aria-label "Remove {label}", and is the only element with hover feedback inside a span tag.
                                • One leading part at most (checkbox, avatar, dot or icon) and one trailing part at most (count or ×).
                                • outline on surfaces (filter bars over white); soft on the canvas, inside inputs and in dense rows where rings would pile up.
                                • accent only for assistant-suggested or brand-linked values, and only a few per view. Never as decoration.
                                • Rows of tags wrap with an 8px gap and align to the start; do not right-align or justify them. One size per row.

                                Accessibility

                                • Selectable: <button type="button" aria-pressed="true|false">, or role="checkbox" aria-checked with the checkbox part. Toggle the aria attribute, not only the data attribute.
                                • Removable: the remove button announces "Remove {label}"; after removing, move focus to the next tag or back to the input.
                                • Both the tag and its remove button are separate Tab stops with their own :focus-visible ring.
                                • Avatars inside tags are decorative (alt=""); the label names the person. A dot needs its meaning in the label ("Online").
                                • Disabled tags stay readable (opacity only). Use disabled on buttons and aria-disabled="true" on span tags.
                                Data display

                                Counter .cn-counteredit

                                A small round pill with a number: the reference's 22px count badge (12px medium, tinted with a ring) after tabs and nav items, or the 14px red disc on a notification icon. Tabular figures, capped at "99+". Numbers only.

                                Use: After a label that it counts: a tab, a nav item, a section title, a filter; or on the corner of an icon button for unread notifications. Not for words (Badge), not for large KPIs (Stat), never as a button.

                                Soft (default)22px, gray-50 with a gray-200 ring, 12px medium gray-700.
                                12
                                HTML
                                <span class="cn-counter" data-tone="neutral" data-variant="soft" data-size="md">12</span>
                                Tones, softneutral · accent · danger · action; each with the ring one step darker than its fill.
                                128 7 2 3
                                HTML
                                <span class="cn-counter" data-tone="neutral" data-variant="soft" data-size="md">128</span> <span class="cn-counter" data-tone="accent" data-variant="soft" data-size="md">7</span> <span class="cn-counter" data-tone="danger" data-variant="soft" data-size="md">2</span> <span class="cn-counter" data-tone="action" data-variant="soft" data-size="md">3</span>
                                Tones, solidOne solid counter per row at most.
                                128 7 2 3
                                HTML
                                <span class="cn-counter" data-tone="neutral" data-variant="solid" data-size="md">128</span> <span class="cn-counter" data-tone="accent" data-variant="solid" data-size="md">7</span> <span class="cn-counter" data-tone="danger" data-variant="solid" data-size="md">2</span> <span class="cn-counter" data-tone="action" data-variant="solid" data-size="md">3</span>
                                Sizes and capxs 14px (the notification disc) · sm 18px · md 22px. Never more than two digits; cap at 99+.
                                3 45 99+
                                HTML
                                <div style="display:flex;align-items:center;gap:var(--cn-space-3)"><span class="cn-counter" data-tone="danger" data-variant="solid" data-size="xs">3</span> <span class="cn-counter" data-tone="neutral" data-variant="soft" data-size="sm">45</span> <span class="cn-counter" data-tone="neutral" data-variant="soft" data-size="md">99+</span></div>

                                Recipes

                                In a nav item, a tab and on an iconThe counter follows the label it counts; only the "needs you" count is action-tinted. data-anchored pins the xs disc to the icon corner.
                                3
                                HTML
                                <div style="display:flex;flex-wrap:wrap;gap:var(--cn-space-3);align-items:center"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Waiting for you</span><span class="cn-counter" data-tone="action" data-variant="soft" data-size="md">3</span></button><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">In review</span><span class="cn-counter" data-tone="neutral" data-variant="soft" data-size="md">12</span></button><span style="position:relative;display:inline-flex"><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="Notifications, 3 unread"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg></button><span class="cn-counter" data-tone="danger" data-variant="solid" data-size="xs" data-anchored>3</span></span></div>

                                Matrix

                                variant ↓ / size →xssmmd
                                solid121212
                                soft121212
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-counter <span> — The pill. Contains only the number (or "99+").

                                Props

                                propvalues (default outlined)meaning
                                toneneutral accent danger actionUrgency, not decoration. neutral (gray) = totals and result counts; action (brand) = things waiting for the user, and the selected tab's count; danger (red) = failures, overdue items and unread notifications; accent = items produced by the assistant.
                                variantsolid softsoft = tinted fill with a 1px ring one step darker (the reference badge); solid = filled, at most one per row or nav to mark the count that matters (and always for the xs notification disc).
                                sizexs sm mdmd 22px tall with 12px medium text (the badge next to tabs and nav items); sm 18px inside buttons and dense rows; xs 14px with 10px bold text, the notification disc on an icon corner.

                                Rules

                                • Numbers only. A word ("New", "Beta", "Live") is a Badge.
                                • Cap at "99+". Never show three or more digits.
                                • Hide the counter at zero; "0" is noise. If the zero is the content ("0 results"), write it as text.
                                • Tone is urgency: neutral for totals, action for what waits for the user, danger for failures and unread notifications, accent for assistant output. Never pick a tone for looks.
                                • At most one solid counter per row, nav or tab bar; the rest are soft. The xs disc is always solid danger.
                                • The counter comes after the label it counts ("Inbox 12"), separated by the parent's gap, never before it.
                                • Stays round at one digit and widens with two; never squash it into a fixed width.
                                • Do not animate count changes (no bounce, no flash). Update the number; announce with aria-live on the region if it matters.

                                Accessibility

                                • The number is plain text and is read as such; give it context with the adjacent label or a visually hidden suffix ("12 unread").
                                • On an icon button, put the count in the button's aria-label ("Notifications, 3 unread") and keep the disc aria-hidden.
                                • Live counts: aria-live="polite" on the containing region (the nav item or tab), not on the counter itself.
                                • Color is reinforcement only; the label next to the counter carries the meaning.
                                Data display

                                Avatar .cn-avataredit

                                A person or company as a small picture in the reference's seven sizes (16 → 64px): photo when there is one, semibold initials on gray-100 or a placeholder icon when there is not, a 0.5px contrast ring, and optional status dot, verified tick or company badge on the corner. Circles are people; squares are companies.

                                Use: Next to a name in tables, lists, comments, assignee fields, menus and headers; with the avatar label group when the name and email sit beside it. Use AvatarGroup for several people in one slot. Not a button: wrap it in a Button or Link when it opens something.

                                Sizesxxs 16 · xs 24 · sm 32 · md 40 · lg 48 · xl 56 · 2xl 64. Semibold initials on gray-100 with the 0.5px contrast ring.
                                HTML
                                <div style="display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--cn-space-4)"><span class="cn-avatar" data-size="xxs" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">M</span></span><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span><span class="cn-avatar" data-size="lg" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span><span class="cn-avatar" data-size="xl" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span><span class="cn-avatar" data-size="2xl" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span></div>
                                Photo, border and statusThe photo gets a faint light rim; data-border adds the 1px outer ring. Dots: online green, offline gray-300, away amber, busy red, each with a 1.5px surface ring.
                                Daniel CostaDaniel CostaSofia Almeida, onlineLucas Ferreira, offlineAisha Khan, awayNoah Berg, busy
                                HTML
                                <div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-4)"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="none"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="Daniel Costa"></span><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" data-border><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="Daniel Costa"></span><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="online"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="Sofia Almeida, online"><span class="cn-avatar__status"></span></span><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="offline"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="Lucas Ferreira, offline"><span class="cn-avatar__status"></span></span><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="away"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="Aisha Khan, away"><span class="cn-avatar__status"></span></span><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="busy"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="Noah Berg, busy"><span class="cn-avatar__status"></span></span></div>
                                Status dot per size6 · 8 · 10 · 12 · 14 · 16px dots.
                                Maya Chen, onlineMaya Chen, onlineMaya Chen, onlineMaya Chen, onlineMaya Chen, onlineMaya Chen, online
                                HTML
                                <div style="display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--cn-space-4)"><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="neutral" data-status="online"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="Maya Chen, online"><span class="cn-avatar__status"></span></span><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="neutral" data-status="online"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="Maya Chen, online"><span class="cn-avatar__status"></span></span><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="online"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="Maya Chen, online"><span class="cn-avatar__status"></span></span><span class="cn-avatar" data-size="lg" data-shape="circle" data-tone="neutral" data-status="online"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="Maya Chen, online"><span class="cn-avatar__status"></span></span><span class="cn-avatar" data-size="xl" data-shape="circle" data-tone="neutral" data-status="online"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="Maya Chen, online"><span class="cn-avatar__status"></span></span><span class="cn-avatar" data-size="2xl" data-shape="circle" data-tone="neutral" data-status="online"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="Maya Chen, online"><span class="cn-avatar__status"></span></span></div>
                                Verified tickThe blue-500 seal with a white check, 10 → 20px, bottom-right.
                                HTML
                                <div style="display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--cn-space-4)"><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Elena Rossi, verified"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""><svg class="cn-avatar__verified" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M8 0l1.9 1.6 2.4-.5.9 2.3 2.3.9-.5 2.4L16 8l-1.6 1.9.5 2.4-2.3.9-.9 2.3-2.4-.5L8 16l-1.9-1.6-2.4.5-.9-2.3-2.3-.9.5-2.4L0 8l1.6-1.9-.5-2.4 2.3-.9.9-2.3 2.4.5z"/><path d="M4.8 8.3l2.1 2.1 4.3-4.4" fill="none" stroke="var(--cn-color-fg-on-action)" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg></span><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Elena Rossi, verified"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""><svg class="cn-avatar__verified" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M8 0l1.9 1.6 2.4-.5.9 2.3 2.3.9-.5 2.4L16 8l-1.6 1.9.5 2.4-2.3.9-.9 2.3-2.4-.5L8 16l-1.9-1.6-2.4.5-.9-2.3-2.3-.9.5-2.4L0 8l1.6-1.9-.5-2.4 2.3-.9.9-2.3 2.4.5z"/><path d="M4.8 8.3l2.1 2.1 4.3-4.4" fill="none" stroke="var(--cn-color-fg-on-action)" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg></span><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Elena Rossi, verified"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""><svg class="cn-avatar__verified" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M8 0l1.9 1.6 2.4-.5.9 2.3 2.3.9-.5 2.4L16 8l-1.6 1.9.5 2.4-2.3.9-.9 2.3-2.4-.5L8 16l-1.9-1.6-2.4.5-.9-2.3-2.3-.9.5-2.4L0 8l1.6-1.9-.5-2.4 2.3-.9.9-2.3 2.4.5z"/><path d="M4.8 8.3l2.1 2.1 4.3-4.4" fill="none" stroke="var(--cn-color-fg-on-action)" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg></span><span class="cn-avatar" data-size="lg" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Elena Rossi, verified"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""><svg class="cn-avatar__verified" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M8 0l1.9 1.6 2.4-.5.9 2.3 2.3.9-.5 2.4L16 8l-1.6 1.9.5 2.4-2.3.9-.9 2.3-2.4-.5L8 16l-1.9-1.6-2.4.5-.9-2.3-2.3-.9.5-2.4L0 8l1.6-1.9-.5-2.4 2.3-.9.9-2.3 2.4.5z"/><path d="M4.8 8.3l2.1 2.1 4.3-4.4" fill="none" stroke="var(--cn-color-fg-on-action)" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg></span><span class="cn-avatar" data-size="xl" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Elena Rossi, verified"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""><svg class="cn-avatar__verified" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M8 0l1.9 1.6 2.4-.5.9 2.3 2.3.9-.5 2.4L16 8l-1.6 1.9.5 2.4-2.3.9-.9 2.3-2.4-.5L8 16l-1.9-1.6-2.4.5-.9-2.3-2.3-.9.5-2.4L0 8l1.6-1.9-.5-2.4 2.3-.9.9-2.3 2.4.5z"/><path d="M4.8 8.3l2.1 2.1 4.3-4.4" fill="none" stroke="var(--cn-color-fg-on-action)" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg></span><span class="cn-avatar" data-size="2xl" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Elena Rossi, verified"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""><svg class="cn-avatar__verified" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M8 0l1.9 1.6 2.4-.5.9 2.3 2.3.9-.5 2.4L16 8l-1.6 1.9.5 2.4-2.3.9-.9 2.3-2.4-.5L8 16l-1.9-1.6-2.4.5-.9-2.3-2.3-.9.5-2.4L0 8l1.6-1.9-.5-2.4 2.3-.9.9-2.3 2.4.5z"/><path d="M4.8 8.3l2.1 2.1 4.3-4.4" fill="none" stroke="var(--cn-color-fg-on-action)" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg></span></div>
                                Company badge and placeholdersA 14px company badge on a person; the person placeholder icon; the square company placeholder (building icon or initials) and a square logo.
                                Lumen
                                HTML
                                <div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-4)"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Tomás Silva at Lumen"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""><span class="cn-avatar__badge"><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='16' fill='%23B4309F'/%3E%3Cpath d='M9 22V10h3.4l3.6 7.2L19.6 10H23v12h-3v-7l-3 6h-1l-3-6v7z' fill='%23fff'/%3E%3C/svg%3E" alt=""></span></span><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Unknown person"><span class="cn-avatar__fallback" aria-hidden="true"><svg class="cn-avatar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg></span></span><span class="cn-avatar" data-size="md" data-shape="square" data-tone="neutral" data-status="none" role="img" aria-label="Lumen"><span class="cn-avatar__fallback" aria-hidden="true"><svg class="cn-avatar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 13.5V3.5a1 1 0 011-1h5a1 1 0 011 1v10M10 6.5h2a1 1 0 011 1v6M1.5 13.5h13M5.5 5h2M5.5 7.5h2M5.5 10h2"/></svg></span></span><span class="cn-avatar" data-size="md" data-shape="square" data-tone="neutral" data-status="none" role="img" aria-label="Lumen"><span class="cn-avatar__fallback" aria-hidden="true">LU</span></span><span class="cn-avatar" data-size="lg" data-shape="square" data-tone="neutral" data-status="none" role="img" aria-label="Lumen"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='16' fill='%23B4309F'/%3E%3Cpath d='M9 22V10h3.4l3.6 7.2L19.6 10H23v12h-3v-7l-3 6h-1l-3-6v7z' fill='%23fff'/%3E%3C/svg%3E" alt="Lumen"></span></div>
                                Label groupThe avatar label group recipe: avatar + name (14px medium) + email (fg-muted); data-size sm / md / lg sets the gap and text sizes.
                                Maya Chen, online
                                Maya Chen
                                Daniel Costa, online
                                Daniel Costa
                                Sofia Almeida
                                HTML
                                <div style="display:flex;flex-direction:column;align-items:flex-start;gap:var(--cn-space-5)"><div class="cn-avatar-label-group" data-size="sm"><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="neutral" data-status="online"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="Maya Chen, online"><span class="cn-avatar__status"></span></span><div class="cn-avatar-label-group__text"><span class="cn-avatar-label-group__name">Maya Chen</span><span class="cn-avatar-label-group__email">maya@lumen.co</span></div></div><div class="cn-avatar-label-group" data-size="md"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="online"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt="Daniel Costa, online"><span class="cn-avatar__status"></span></span><div class="cn-avatar-label-group__text"><span class="cn-avatar-label-group__name">Daniel Costa</span><span class="cn-avatar-label-group__email">daniel@lumen.co</span></div></div><div class="cn-avatar-label-group" data-size="lg"><span class="cn-avatar" data-size="lg" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Sofia Almeida"><span class="cn-avatar__fallback" aria-hidden="true">SA</span></span><div class="cn-avatar-label-group__text"><span class="cn-avatar-label-group__name">Sofia Almeida</span><span class="cn-avatar-label-group__email">sofia@lumen.co</span></div></div></div>
                                Tones and the focusable triggerneutral for people; the other tones only when the entity has that state. A button avatar gets the focus ring; .cn-avatar-add is the dashed add button.
                                HTML
                                <div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-4)"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="accent" data-status="none" role="img" aria-label="Lumen assistant"><span class="cn-avatar__fallback" aria-hidden="true">AI</span></span><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="info" data-status="none" role="img" aria-label="New contact"><span class="cn-avatar__fallback" aria-hidden="true">NC</span></span><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="success" data-status="none" role="img" aria-label="Verified buyer"><span class="cn-avatar__fallback" aria-hidden="true">VB</span></span><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="warning" data-status="none" role="img" aria-label="Pending review"><span class="cn-avatar__fallback" aria-hidden="true">PR</span></span><button type="button" class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" aria-label="Open account menu" aria-haspopup="menu"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></button><button type="button" class="cn-avatar-add" aria-label="Add teammate"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg></button></div>

                                Matrix

                                tone ↓ / size →xxsxssmmdlgxl2xl
                                neutral
                                accent
                                info
                                success
                                warning
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-avatar <span> — Sized box, position relative, overflow visible so the badges can sit on the edge; draws the 0.5px inner contrast ring. Carries role="img" + aria-label when showing initials. Add data-border for the 1px outer ring, tabindex or make it a button when focusable.
                                • .cn-avatar__image <img> · optional — The photo, cropped with object-fit cover, clipped by the root radius, with the reference's faint 1px light rim at the top and bottom edge. Needs alt.
                                • .cn-avatar__fallback <span> · optional — Initials (1–2 letters, semibold, 12 → 24px) centered on the gray-100 fill. Shown when there is no image.
                                • .cn-avatar__icon <svg> · optional — Placeholder icon (a person, or a building for companies) inside the fallback: 12 → 32px, fg-subtle.
                                • .cn-avatar__status <span> · optional — Presence dot at the bottom-right (6 → 16px) with a 1.5px surface ring so it reads on any photo. Hidden unless data-status is a value other than none.
                                • .cn-avatar__verified <svg> · optional — The blue verified tick at the bottom-right (10 → 20px): a blue-500 seal with a white check.
                                • .cn-avatar__badge <span> · optional — Company-logo badge at the bottom-right (8 → 20px): a round brand-50 tile with a 1.5px surface ring holding a small logo image.

                                Props

                                propvalues (default outlined)meaning
                                sizexxs xs sm md lg xl 2xlOuter size: xxs 16px (inline in text, tags), xs 24px (dense tables, menus, kanban cards), sm 32px (lists, table rows), md 40px (default: account cards, comments, table rows with two lines), lg 48px (headers, drawers), xl 56px and 2xl 64px (profile and settings pages).
                                shapecircle squarecircle = a person; square (radius 6) = a company, workspace or integration. Never mix shapes in one list.
                                toneneutral accent info success warningTint of the initials fallback. neutral (gray-100 with gray-500 initials, the reference) for people; accent only for the product's own assistant; info/success/warning only when the entity itself has that state. Never assign colors per person.
                                statusnone online away busy offlinePresence dot: none hides it (default); online = green-500, away = amber, busy = red, offline = gray-300. Only for real-time presence.

                                States

                                stateselectormeaning
                                focus:focus-visibleKeyboard focus when the avatar is a button or link (account menu trigger): the 4px brand ring. (make the avatar a <button> or <a>, or add tabindex="0")

                                Rules

                                • Circle = a person. Square (radius 6) = a company, a workspace or an integration. One shape per list.
                                • Initials: first + last name (2 letters) from xs up; a single letter at xxs. Semibold, gray-500 on gray-100.
                                • Fallback tone is neutral for people. accent is reserved for the product's assistant. Never give people random colors.
                                • Show the status dot only for real-time presence that matters; never as decoration; data-status="none" otherwise. The verified tick and the company badge are mutually exclusive with the dot.
                                • Photos are cropped with cover, never stretched; the only frame is data-border (1px outer ring) on profile pages.
                                • Sizes follow the slot: xxs inline in text and tags, xs in dense tables and menus, sm in list rows, md in account cards and 72px table rows, lg in headers and drawers, xl / 2xl on profile pages.
                                • Next to a name and email use the avatar label group (.cn-avatar-label-group) so the text sizes and gaps match the avatar size.
                                • The avatar is not a control unless it is the account menu trigger: then it is a <button aria-haspopup="menu"> and gets the focus ring; do not add hover styles.
                                • Always pair with the name in text nearby except in tight groups; the picture is never the only identification.

                                Accessibility

                                • Photo: alt is the person's name when the name is not adjacent text; alt="" when it is, to avoid reading the name twice.
                                • Initials: put role="img" and aria-label="Full name" on the root and aria-hidden="true" on the initials span.
                                • Presence and verification must be available as text: include them in the alt or aria-label ("Maya Chen, online", "Elena Rossi, verified"). The dot and tick alone are not accessible.
                                • When clickable, the avatar is a <button> or <a> with an aria-label and shows the focus ring; a wrapping Link works too.
                                • The add button (.cn-avatar-add) needs aria-label ("Add teammate").
                                Data display

                                AvatarGroup .cn-avatar-groupedit

                                A row of overlapping avatars for the people on one thing, each separated by a 1.5px surface ring, ending in a "+3" gray disc when there are more than fit and optionally the dashed add button.

                                Use: Assignees, participants, watchers: several people in one slot (a table cell, a card footer, a header). One person is an Avatar, not a group of one. Not for companies or integrations.

                                Three people40px avatars overlapping by 8px, each with the 1.5px surface ring; the first is drawn on top.
                                HTML
                                <div class="cn-avatar-group" data-size="md" role="group" aria-label="Assigned to Maya Chen, Daniel Costa, Sofia Almeida"><span class="cn-avatar cn-avatar-group__item" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Maya Chen"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""></span><span class="cn-avatar cn-avatar-group__item" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span></span><span class="cn-avatar cn-avatar-group__item" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Sofia Almeida"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""></span></div>
                                Five visible, then overflowNever more than five avatars; the gray disc carries the rest.
                                +3
                                HTML
                                <div class="cn-avatar-group" data-size="md" role="group" aria-label="Assigned to Maya Chen, Daniel Costa, Sofia Almeida, Lucas Ferreira, Aisha Khan and 3 others"><span class="cn-avatar cn-avatar-group__item" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Maya Chen"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""></span><span class="cn-avatar cn-avatar-group__item" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span></span><span class="cn-avatar cn-avatar-group__item" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Sofia Almeida"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""></span><span class="cn-avatar cn-avatar-group__item" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Lucas Ferreira"><span class="cn-avatar__fallback" aria-hidden="true">LF</span></span><span class="cn-avatar cn-avatar-group__item" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Aisha Khan"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""></span><span class="cn-avatar-group__overflow">+3</span></div>
                                Sizesxs 24 · sm 32 · md 40 · lg 48; the overlap is 4 / 8 / 8 / 12px.
                                +2
                                +2
                                +2
                                +2
                                HTML
                                <div style="display:flex;align-items:center;gap:var(--cn-space-6);flex-wrap:wrap"><div class="cn-avatar-group" data-size="xs" role="group" aria-label="Assigned to Maya Chen, Daniel Costa, Sofia Almeida"><span class="cn-avatar cn-avatar-group__item" data-size="xs" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span><span class="cn-avatar cn-avatar-group__item" data-size="xs" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span></span><span class="cn-avatar cn-avatar-group__item" data-size="xs" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Sofia Almeida"><span class="cn-avatar__fallback" aria-hidden="true">SA</span></span><span class="cn-avatar-group__overflow">+2</span></div> <div class="cn-avatar-group" data-size="sm" role="group" aria-label="Assigned to Maya Chen, Daniel Costa, Sofia Almeida"><span class="cn-avatar cn-avatar-group__item" data-size="sm" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span><span class="cn-avatar cn-avatar-group__item" data-size="sm" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span></span><span class="cn-avatar cn-avatar-group__item" data-size="sm" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Sofia Almeida"><span class="cn-avatar__fallback" aria-hidden="true">SA</span></span><span class="cn-avatar-group__overflow">+2</span></div> <div class="cn-avatar-group" data-size="md" role="group" aria-label="Assigned to Maya Chen, Daniel Costa, Sofia Almeida"><span class="cn-avatar cn-avatar-group__item" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span><span class="cn-avatar cn-avatar-group__item" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span></span><span class="cn-avatar cn-avatar-group__item" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Sofia Almeida"><span class="cn-avatar__fallback" aria-hidden="true">SA</span></span><span class="cn-avatar-group__overflow">+2</span></div> <div class="cn-avatar-group" data-size="lg" role="group" aria-label="Assigned to Maya Chen, Daniel Costa, Sofia Almeida"><span class="cn-avatar cn-avatar-group__item" data-size="lg" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span><span class="cn-avatar cn-avatar-group__item" data-size="lg" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span></span><span class="cn-avatar cn-avatar-group__item" data-size="lg" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Sofia Almeida"><span class="cn-avatar__fallback" aria-hidden="true">SA</span></span><span class="cn-avatar-group__overflow">+2</span></div></div>
                                With the add buttonThe dashed add button sits 8px after the row, same size as the avatars.
                                HTML
                                <div class="cn-avatar-group" data-size="md" role="group" aria-label="Maya Chen, Daniel Costa, Sofia Almeida, Lucas Ferreira"><span class="cn-avatar cn-avatar-group__item" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Maya Chen"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""></span><span class="cn-avatar cn-avatar-group__item" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span></span><span class="cn-avatar cn-avatar-group__item" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Sofia Almeida"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""></span><span class="cn-avatar cn-avatar-group__item" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Lucas Ferreira"><span class="cn-avatar__fallback" aria-hidden="true">LF</span></span><button type="button" class="cn-avatar-group__add" aria-label="Add person"><svg class="cn-avatar-group__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg></button></div>
                                Overflow as a buttonOnly when clicking reveals the full list.
                                HTML
                                <div class="cn-avatar-group" data-size="sm" role="group" aria-label="8 participants"><span class="cn-avatar cn-avatar-group__item" data-size="sm" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span><span class="cn-avatar cn-avatar-group__item" data-size="sm" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span></span><span class="cn-avatar cn-avatar-group__item" data-size="sm" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Sofia Almeida"><span class="cn-avatar__fallback" aria-hidden="true">SA</span></span><span class="cn-avatar cn-avatar-group__item" data-size="sm" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Lucas Ferreira"><span class="cn-avatar__fallback" aria-hidden="true">LF</span></span><button type="button" class="cn-avatar-group__overflow" aria-label="Show all 8 participants">+4</button></div>

                                Matrix

                                size
                                xs
                                sm
                                md
                                lg
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-avatar-group <div> — Inline-flex row, role="group" with an aria-label that summarizes who is in it.
                                • .cn-avatar-group__item <span> — An Avatar root that also carries this class. Gets the 1.5px surface ring and the negative overlap; the group's data-size overrides the avatar size.
                                • .cn-avatar-group__overflow <span> · optional — The "+N" disc, same size as the avatars, gray-50 with a gray-200 ring and 12–16px semibold gray-500 text. A <button> when it opens the full list.
                                • .cn-avatar-group__add <button> · optional — The add button: a dashed gray-300 circle on white with a plus icon, same size as the avatars, 8px after the row.
                                • .cn-avatar-group__icon <svg> · optional — The 16 / 20px plus icon inside the add button.

                                Props

                                propvalues (default outlined)meaning
                                sizexs sm md lgAvatar size inside the group: xs 24px (table cells, kanban cards), sm 32px (list rows, card footers), md 40px (default: card headers), lg 48px (page headers). Pass the same value as data-size on each avatar so initials scale too.

                                Rules

                                • Maximum five visible avatars. Beyond that, the overflow disc shows the remaining count ("+3"); never a second row.
                                • Source order is relevance order: owner first, then assignees by recency. The first avatar is drawn on top so it is fully visible.
                                • One size and one shape per group; pass the group size as data-size on every avatar as well.
                                • Initials inside a group are fine at sm and up; at xs use a single letter so the 4px overlap does not clip them. Photos are unaffected.
                                • The overflow disc is a <span> unless clicking it opens the full list; then it is a <button> with aria-label "Show all N people".
                                • The add button belongs to groups the user can edit (assignees, members); never in read-only cells.
                                • The rings are bg-surface: place groups on surface or canvas, not on tinted fills.
                                • No status dots inside a group; the overlap hides them. Show presence in the expanded list instead.
                                • A group of one is an Avatar. A group of zero is nothing (or "Unassigned" text), never an empty disc.

                                Accessibility

                                • root has role="group" and an aria-label summarizing the members ("Assigned to Maya Chen, Daniel Costa and 3 others") so it is not read one avatar at a time; each avatar keeps its own label.
                                • The overflow count is real text and is announced; as a button it needs an aria-label with the total count.
                                • The add button needs aria-label ("Add person") and is the only focusable element besides an overflow button.
                                • Overlap is visual only; the DOM order is the meaningful order.
                                Data display

                                AgentPresence .cn-agent-presenceedit

                                Shows an AI agent as a teammate: avatar with a status dot, the name, a state pill and a live line saying what it is doing right now. The accent color appears only here and only when an agent is working.

                                Use: Use wherever a user needs to know whether an agent is active and on what: the team rail, a conversation header, a run list, a kanban card. Not for human users (use Avatar) and not for static labels (use Badge). The activity line must be live; if there is nothing current, show the last run time instead.

                                Row, working
                                VeraWorkingReading 3 replies from Nakamura Trading Co.…2m
                                HTML
                                <div class="cn-agent-presence" data-variant="row" data-size="md" data-status="working" style="width:420px"><span class="cn-agent-presence__avatar"><svg class="cn-icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 1.5l1.6 4.9 4.9 1.6-4.9 1.6L8 14.5l-1.6-4.9L1.5 8l4.9-1.6z"/></svg><span class="cn-agent-presence__dot"></span></span><span class="cn-agent-presence__name">Vera</span><span class="cn-agent-presence__state">Working</span><span class="cn-agent-presence__activity">Reading 3 replies from Nakamura Trading Co.…</span><span class="cn-agent-presence__time">2m</span></div>
                                Row, waiting for you
                                IRIrisWaiting for you2 drafts ready for approval · Hoffmann GmbH, Lagos Fresh14m
                                HTML
                                <div class="cn-agent-presence" data-variant="row" data-size="md" data-status="waiting" style="width:420px"><span class="cn-agent-presence__avatar">IR<span class="cn-agent-presence__dot"></span></span><span class="cn-agent-presence__name">Iris</span><span class="cn-agent-presence__state">Waiting for you</span><span class="cn-agent-presence__activity">2 drafts ready for approval · Hoffmann GmbH, Lagos Fresh</span><span class="cn-agent-presence__time">14m</span></div>
                                Row, idle
                                THTheoIdleLast run 40 min ago · verified 38 companies40m
                                HTML
                                <div class="cn-agent-presence" data-variant="row" data-size="md" data-status="idle" style="width:420px"><span class="cn-agent-presence__avatar">TH<span class="cn-agent-presence__dot"></span></span><span class="cn-agent-presence__name">Theo</span><span class="cn-agent-presence__state">Idle</span><span class="cn-agent-presence__activity">Last run 40 min ago · verified 38 companies</span><span class="cn-agent-presence__time">40m</span></div>
                                Row, offline
                                KAKaiOfflinePaused until Monday 08:002d
                                HTML
                                <div class="cn-agent-presence" data-variant="row" data-size="md" data-status="offline" style="width:420px"><span class="cn-agent-presence__avatar">KA<span class="cn-agent-presence__dot"></span></span><span class="cn-agent-presence__name">Kai</span><span class="cn-agent-presence__state">Offline</span><span class="cn-agent-presence__activity">Paused until Monday 08:00</span><span class="cn-agent-presence__time">2d</span></div>
                                ChipsChips carry only avatar, dot and name. Use them inline: “Assigned to [Vera]”.
                                Vera IRIris THTheo
                                HTML
                                <span class="cn-agent-presence" data-variant="chip" data-size="md" data-status="working"><span class="cn-agent-presence__avatar"><svg class="cn-icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 1.5l1.6 4.9 4.9 1.6-4.9 1.6L8 14.5l-1.6-4.9L1.5 8l4.9-1.6z"/></svg><span class="cn-agent-presence__dot"></span></span><span class="cn-agent-presence__name">Vera</span></span> <span class="cn-agent-presence" data-variant="chip" data-size="md" data-status="waiting"><span class="cn-agent-presence__avatar">IR<span class="cn-agent-presence__dot"></span></span><span class="cn-agent-presence__name">Iris</span></span> <span class="cn-agent-presence" data-variant="chip" data-size="sm" data-status="idle"><span class="cn-agent-presence__avatar">TH<span class="cn-agent-presence__dot"></span></span><span class="cn-agent-presence__name">Theo</span></span>
                                Card, working, sm
                                VeraWorkingDrafting a reply to Marisco del Sur in Spanish…45s
                                HTML
                                <div class="cn-agent-presence" data-variant="card" data-size="sm" data-status="working" style="width:320px"><span class="cn-agent-presence__avatar"><svg class="cn-icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 1.5l1.6 4.9 4.9 1.6-4.9 1.6L8 14.5l-1.6-4.9L1.5 8l4.9-1.6z"/></svg><span class="cn-agent-presence__dot"></span></span><span class="cn-agent-presence__name">Vera</span><span class="cn-agent-presence__state">Working</span><span class="cn-agent-presence__activity">Drafting a reply to Marisco del Sur in Spanish…</span><span class="cn-agent-presence__time">45s</span></div>

                                Recipes

                                Team railRows stack with no divider; waiting agents sort above working ones because they need the user.
                                VeraWorkingSearching importers in South Korea…6m
                                IRIrisWaiting for youNeeds the price list for Hoffmann GmbH1h
                                THTheoIdleLast run today 07:003h
                                HTML
                                <div style="display:flex;flex-direction:column;width:420px;border:1px solid var(--cn-color-border-default);border-radius:var(--cn-radius-xl);background:var(--cn-color-bg-surface);padding:var(--cn-space-2) var(--cn-space-4)"><div class="cn-agent-presence" data-variant="row" data-size="md" data-status="working"><span class="cn-agent-presence__avatar"><svg class="cn-icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 1.5l1.6 4.9 4.9 1.6-4.9 1.6L8 14.5l-1.6-4.9L1.5 8l4.9-1.6z"/></svg><span class="cn-agent-presence__dot"></span></span><span class="cn-agent-presence__name">Vera</span><span class="cn-agent-presence__state">Working</span><span class="cn-agent-presence__activity">Searching importers in South Korea…</span><span class="cn-agent-presence__time">6m</span></div><div class="cn-agent-presence" data-variant="row" data-size="md" data-status="waiting"><span class="cn-agent-presence__avatar">IR<span class="cn-agent-presence__dot"></span></span><span class="cn-agent-presence__name">Iris</span><span class="cn-agent-presence__state">Waiting for you</span><span class="cn-agent-presence__activity">Needs the price list for Hoffmann GmbH</span><span class="cn-agent-presence__time">1h</span></div><div class="cn-agent-presence" data-variant="row" data-size="md" data-status="idle"><span class="cn-agent-presence__avatar">TH<span class="cn-agent-presence__dot"></span></span><span class="cn-agent-presence__name">Theo</span><span class="cn-agent-presence__state">Idle</span><span class="cn-agent-presence__activity">Last run today 07:00</span><span class="cn-agent-presence__time">3h</span></div></div>

                                Matrix

                                variant ↓ / size →smmd
                                chip
                                VeraWorkingReading 3 replies from Nakamura Trading Co.…2m
                                VeraWorkingReading 3 replies from Nakamura Trading Co.…2m
                                row
                                VeraWorkingReading 3 replies from Nakamura Trading Co.…2m
                                VeraWorkingReading 3 replies from Nakamura Trading Co.…2m
                                card
                                VeraWorkingReading 3 replies from Nakamura Trading Co.…2m
                                VeraWorkingReading 3 replies from Nakamura Trading Co.…2m
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-agent-presence <div> — The element. Chip is inline; row and card are a grid: avatar | name + state | time on the first line, activity on the second.
                                • .cn-agent-presence__avatar <span> — 24px circle (20px in sm) in the accent wash with the agent’s initials or the spark icon in fg-accent. Contains the dot.
                                • .cn-agent-presence__dot <span> — 8px status dot at the avatar’s bottom-right with a 2px surface ring. Pulses (halo) only when working.
                                • .cn-agent-presence__name <span> — label-sm, the agent’s name ("Vera", "Iris"). Never a role or a model name.
                                • .cn-agent-presence__state <span> · optional — Kicker-style pill: "Working", "Waiting for you", "Idle", "Offline". Hidden in chip.
                                • .cn-agent-presence__activity <span> · optional — body-xs muted, truncated, the live "now" line: "Reading 3 replies from Nakamura…". Hidden in chip.
                                • .cn-agent-presence__time <span> · optional — code-sm subtle, how long the current state has lasted ("2m", "40m"). Hidden in chip.

                                Props

                                propvalues (default outlined)meaning
                                variantchip row cardchip = inline pill with avatar, name and dot only (inside text, headers, kanban cards); row = one line in a list with the state pill, activity and time (default); card = the row on a bordered surface with padding, for a team grid or a dashboard.
                                sizesm mdmd = 24px avatar, label-sm name (default); sm = 20px avatar, label-xs name, for dense tables and chips inside text.
                                statusworking waiting idle offlineworking = the agent is acting right now: accent dot with a pulse and an accent pill; waiting = it needs the user (approval, a file): warning dot and pill; idle = available, nothing running: neutral; offline = paused or disconnected: muted dot and the whole element at 70% opacity.

                                Rules

                                • Orchid/accent means "an agent is working". This is the only place accent is used as a status; never use it for human users or for success.
                                • The activity line is live and concrete: verb + object + count ("Reading 3 replies from Nakamura…"). It updates in place without re-rendering the row.
                                • When nothing is happening, the activity line shows the last run ("Last run 40 min ago · verified 38 companies"), never "Nothing to do".
                                • Agents have a name and a task, not a personality: no "I", no emoji, no "thinking…", no model names.
                                • State pill text is fixed: Working / Waiting for you / Idle / Offline. Do not invent states; put details in the activity line.
                                • waiting sorts above working in any list: it is the one status that needs the user.
                                • Chips carry no activity; if the reader needs to know what the agent is doing, use a row.
                                • The pulse is the only motion: 1.8s halo on the dot while working, off under prefers-reduced-motion.

                                Accessibility

                                • The status is text (the pill) and never only the dot color; chips keep the status in an aria-label on the root ("Vera, working").
                                • The activity line is a polite live region (aria-live="polite" on the list, not on each row) so screen readers hear changes without interruption.
                                • Time is relative and short ("2m"); pair it with a title attribute carrying the absolute time.
                                • Initials in the avatar are aria-hidden when the name is visible next to them.
                                Data display

                                Stat .cn-statedit

                                A metric readout with a compact mono label and one semibold tabular number, a change indicator with an arrow (green up, red down) or as a pill, a "vs last month" note, optionally a featured icon above and a "View report" footer. Direction is a fact; whether it is good is a separate attribute.

                                Use: Dashboards and page headers that summarize a workspace ("Active users 2,420", "Conversion 4.2%"). Rows of 3–4 tiles. Not for counts inside navigation (Counter) or for a value inside a table (plain tabular text).

                                Metric card (default)White card with a fine outline, 20 × 24px padding; 14px medium label, a token-sized semibold value, green arrow + "12%", muted "vs last month".
                                Active users
                                2,420
                                12%
                                vs last month
                                HTML
                                <div class="cn-stat" data-size="md" data-variant="card" data-trend="up" style="width:280px"><div class="cn-stat__label">Active users</div><div class="cn-stat__value">2,420</div><div class="cn-stat__delta"><svg class="cn-stat__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 13V3M4 7l4-4 4 4"/></svg><span>12%</span></div><div class="cn-stat__description">vs last month</div></div>
                                Row of threeOne row, equal widths, same size. The labels line up.
                                Total users
                                2,420
                                40%
                                vs last month
                                Active sessions
                                1,210
                                10%
                                vs last month
                                Conversion rate
                                4.2%
                                0.8 pts
                                vs last month
                                HTML
                                <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--cn-space-6);width:100%"><div class="cn-stat" data-size="md" data-variant="card" data-trend="up"><div class="cn-stat__label">Total users</div><div class="cn-stat__value">2,420</div><div class="cn-stat__delta"><svg class="cn-stat__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 13V3M4 7l4-4 4 4"/></svg><span>40%</span></div><div class="cn-stat__description">vs last month</div></div><div class="cn-stat" data-size="md" data-variant="card" data-trend="down"><div class="cn-stat__label">Active sessions</div><div class="cn-stat__value">1,210</div><div class="cn-stat__delta"><svg class="cn-stat__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 3v10M4 9l4 4 4-4"/></svg><span>10%</span></div><div class="cn-stat__description">vs last month</div></div><div class="cn-stat" data-size="md" data-variant="card" data-trend="up"><div class="cn-stat__label">Conversion rate</div><div class="cn-stat__value">4.2%</div><div class="cn-stat__delta"><svg class="cn-stat__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 13V3M4 7l4-4 4 4"/></svg><span>0.8 pts</span></div><div class="cn-stat__description">vs last month</div></div></div>
                                With icon and actionsA 40px modern FeaturedIcon above the label; the delta as a tinted pill (data-badge); "View report" in a footer on a hairline.
                                Views 24h
                                18,204
                                6%
                                vs yesterday
                                Revenue
                                $32,480
                                2%
                                vs last month
                                HTML
                                <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:var(--cn-space-6);width:100%;max-width:560px"><div class="cn-stat" data-size="md" data-variant="card" data-trend="up"><div class="cn-stat__media"><span class="cn-featured-icon" data-theme="modern" data-tone="gray" data-size="md" data-shape="square"><svg class="cn-featured-icon__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg></span></div><div class="cn-stat__label">Views 24h</div><div class="cn-stat__value">18,204</div><div class="cn-stat__delta"><svg class="cn-stat__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 13V3M4 7l4-4 4 4"/></svg><span>6%</span></div><div class="cn-stat__description">vs yesterday</div><div class="cn-stat__actions"><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">View report</span><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg></button></div></div><div class="cn-stat" data-size="md" data-variant="card" data-trend="down"><div class="cn-stat__media"><span class="cn-featured-icon" data-theme="modern" data-tone="gray" data-size="md" data-shape="square"><svg class="cn-featured-icon__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg></span></div><div class="cn-stat__label">Revenue</div><div class="cn-stat__value">$32,480</div><div class="cn-stat__delta" data-badge><svg class="cn-stat__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 3v10M4 9l4 4 4-4"/></svg><span>2%</span></div><div class="cn-stat__description">vs last month</div><div class="cn-stat__actions"><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">View report</span><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg></button></div></div></div>
                                Sizes (plain)sm 24px · md 30px · lg 36px values.
                                Conversion
                                4.2%
                                0.8 pts
                                Conversion
                                4.2%
                                0.8 pts
                                Conversion
                                4.2%
                                0.8 pts
                                vs last month
                                HTML
                                <div style="display:flex;gap:var(--cn-space-10);align-items:flex-end;flex-wrap:wrap"><div class="cn-stat" data-size="sm" data-variant="plain" data-trend="up"><div class="cn-stat__label">Conversion</div><div class="cn-stat__value">4.2%</div><div class="cn-stat__delta"><svg class="cn-stat__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 13V3M4 7l4-4 4 4"/></svg><span>0.8 pts</span></div></div><div class="cn-stat" data-size="md" data-variant="plain" data-trend="up"><div class="cn-stat__label">Conversion</div><div class="cn-stat__value">4.2%</div><div class="cn-stat__delta"><svg class="cn-stat__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 13V3M4 7l4-4 4 4"/></svg><span>0.8 pts</span></div></div><div class="cn-stat" data-size="lg" data-variant="plain" data-trend="up"><div class="cn-stat__label">Conversion</div><div class="cn-stat__value">4.2%</div><div class="cn-stat__delta"><svg class="cn-stat__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 13V3M4 7l4-4 4 4"/></svg><span>0.8 pts</span></div><div class="cn-stat__description">vs last month</div></div></div>
                                Direction is not sentimentThe arrow says which way it moved; data-sentiment says whether that is good.
                                Avg. response time
                                2.1h
                                18%
                                vs last month
                                Bounce rate
                                4.2%
                                0.8 pts
                                vs last month
                                HTML
                                <div style="display:flex;gap:var(--cn-space-6);flex-wrap:wrap"><div class="cn-stat" data-size="md" data-variant="card" data-trend="down" data-sentiment="good" style="width:260px"><div class="cn-stat__label">Avg. response time</div><div class="cn-stat__value">2.1h</div><div class="cn-stat__delta"><svg class="cn-stat__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 3v10M4 9l4 4 4-4"/></svg><span>18%</span></div><div class="cn-stat__description">vs last month</div></div><div class="cn-stat" data-size="md" data-variant="card" data-trend="up" data-sentiment="bad" style="width:260px"><div class="cn-stat__label">Bounce rate</div><div class="cn-stat__value">4.2%</div><div class="cn-stat__delta"><svg class="cn-stat__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 13V3M4 7l4-4 4 4"/></svg><span>0.8 pts</span></div><div class="cn-stat__description">vs last month</div></div></div>

                                Matrix

                                variant ↓ / size →smmdlg
                                plain
                                Active users
                                2,420
                                12%
                                vs last month
                                Active users
                                2,420
                                12%
                                vs last month
                                Active users
                                2,420
                                12%
                                vs last month
                                card
                                Active users
                                2,420
                                12%
                                vs last month
                                Active users
                                2,420
                                12%
                                vs last month
                                Active users
                                2,420
                                12%
                                vs last month
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-stat <div> — Column of media, label, value, delta + description, actions. Plain, or framed as the metric card (white, gray-200 ring, flat edge and token-defined corners, 20 × 24px padding).
                                • .cn-stat__media <div> · optional — Optional 40px FeaturedIcon (modern, gray) above the label, 16px before it.
                                • .cn-stat__label <div> — What is measured, 14px medium fg-muted. 1–3 words ("Active users").
                                • .cn-stat__value <div> — The number, semibold, tabular figures: 24 / 30 / 36px by size. Include the unit in the string ("41%", "2.1h", "$412k").
                                • .cn-stat__delta <div> · optional — Optional change vs the previous period: 16px arrow + 14px medium text ("12%"). Colored by trend and sentiment; data-badge renders it as a tinted pill.
                                • .cn-stat__icon <svg> · optional — 16px arrow inside the delta: up, down or flat, matching data-trend.
                                • .cn-stat__description <div> · optional — Optional 14px fg-muted note after the delta: period, source or definition ("vs last month").
                                • .cn-stat__actions <div> · optional — Optional footer on a hairline (bleeds to the card edges, 16 × 24px padding) holding a link-color Button ("View report").

                                Props

                                propvalues (default outlined)meaning
                                sizesm md lgSize of the value: sm = 24/32 semibold (inside cards and drawers); md = 30/38 (the default readout, dashboard rows); lg = 36/44 (one hero number per page).
                                variantplain cardcard = the metric card: white, gray-200 ring, flat edge and token-defined corners, 20 × 24px padding (standalone tiles on the canvas); plain = no frame, for stats inside a Card, a header or next to a chart.
                                trendup down flatDirection of the delta: up = increased (green), down = decreased (red), flat = unchanged (muted). Direction only; add data-sentiment="good|bad" when up is bad or down is good.

                                Rules

                                • Label is 14px medium muted, 1–3 words, names the metric ("Active users"), never the value type ("Count").
                                • The value carries its unit in the string ("41%", "2.1h", "$412k") and uses tabular figures; format with the locale, max 5 significant characters plus unit.
                                • data-trend is the direction of change (the arrow), not a judgement. When up is bad (bounce rate, response time) or down is good, add data-sentiment="good|bad" so the color tells the truth.
                                • The delta is the change ("12%", "0.8 pts") and the description names the period ("vs last month"); no delta at all beats a delta without a period.
                                • Rows of stats: 3–4 tiles, equal width, same size and variant, 24px apart, labels aligned on one baseline. Never a grid of nine tiles.
                                • One lg (hero) stat per page at most; md in dashboard rows; sm inside cards, drawers and comparisons.
                                • Use variant="card" on the canvas; plain inside a Card, a page header or next to a chart (ChartFrame).
                                • The only action is the "View report" link in the footer; no buttons inside the tile. If the whole tile opens a report, wrap it in a Link.

                                Accessibility

                                • Reading order is label, value, delta, description; the DOM must match so screen readers announce "Active users, 2,420, up 12% vs last month".
                                • The arrow is decorative (aria-hidden); the delta text must state the direction in words or sign ("+12%", "−18%"), never rely on the arrow or color.
                                • Use a real heading only when the stat is a section of its own; inside a row of tiles keep them as divs so the page outline stays clean.
                                • Ensure the value text is selectable and not an image; no SVG numbers.
                                Data display

                                Table .cn-tableedit

                                Rows of records in the reference table: a gray-50 header row with 12px medium labels, 64px rows (72 with avatars) on gray-200 hairlines, 14px gray-600 cells with ink medium primary text, a checkbox column, sortable headers and gray-50 hover / selected rows; optionally framed as a card (radius 12, ring, shadow-xs).

                                Use: Collections of similar records that people scan and compare (team members, customers, invoices, files). For rows with one line of text and an avatar use List; for key/value pairs use DescriptionList; for a handful of visual items use Cards. Put the table in a Card with a CardHeader and a Pagination footer, or use variant="card" standalone.

                                Team members (card, comfortable, sorted, one selected)Component previewThe reference team table: 44px gray-50 header, 72px rows with a 40px avatar and two lines (primary ink medium, secondary muted), a 44px checkbox column, Name sorted ascending, Role sortable on hover, a help icon on Status, ghost + link actions.
                                StatusEmail addressTeamsActions
                                Maya Chen@maya
                                ActiveProduct Managermaya@lumen.co
                                DesignProduct
                                Daniel Costa@daniel
                                ActiveEngineeringdaniel@lumen.co
                                DesignProduct+2
                                Sofia Almeida@sofia
                                OfflineDesignsofia@lumen.co
                                DesignProduct
                                Lucas Ferreira@lucas
                                ActiveFrontendlucas@lumen.co
                                DesignProduct+2
                                Aisha Khan@aisha
                                ActiveBackendaisha@lumen.co
                                DesignProduct
                                HTML
                                <div style="width:100%;overflow-x:auto" role="region" tabindex="0" aria-label="Scrollable table"><table class="cn-table" data-density="comfortable" data-variant="card" data-interactive><thead class="cn-table__head"><tr class="cn-table__row"><th class="cn-table__header-cell cn-table__checkbox" scope="col"><label class="cn-checkbox" data-size="md"><input class="cn-checkbox__input" type="checkbox" aria-label="Select all"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></th><th class="cn-table__header-cell" scope="col" aria-sort="ascending"><button type="button">Name<svg class="cn-table__sort" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 3v10M4 9l4 4 4-4"/></svg></button></th><th class="cn-table__header-cell" scope="col"><span>Status</span><svg class="cn-table__help" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg></th><th class="cn-table__header-cell" scope="col"><button type="button">Role<svg class="cn-table__sort" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 6l3-3 3 3M5 10l3 3 3-3"/></svg></button></th><th class="cn-table__header-cell" scope="col">Email address</th><th class="cn-table__header-cell" scope="col">Teams</th><th class="cn-table__header-cell cn-table__actions" scope="col"><span class="cn-sr-only">Actions</span></th></tr></thead><tbody class="cn-table__body"><tr class="cn-table__row"><td class="cn-table__cell cn-table__checkbox"><label class="cn-checkbox" data-size="md"><input class="cn-checkbox__input" type="checkbox" aria-label="Select Maya Chen"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-avatar-label-group" data-size="md"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" aria-hidden="true"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""></span><div class="cn-avatar-label-group__text"><span class="cn-table__primary">Maya Chen</span><span class="cn-table__secondary">@maya</span></div></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Active</span></td><td class="cn-table__cell">Product Manager</td><td class="cn-table__cell">maya@lumen.co</td><td class="cn-table__cell"><div style="display:flex;gap:var(--cn-space-1)"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Design</span><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">Product</span></div></td><td class="cn-table__cell cn-table__actions"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Delete</span></button><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Edit</span></button></td></tr><tr class="cn-table__row" data-selected><td class="cn-table__cell cn-table__checkbox"><label class="cn-checkbox" data-size="md"><input class="cn-checkbox__input" type="checkbox" checked aria-label="Select Daniel Costa"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-avatar-label-group" data-size="md"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" aria-hidden="true"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""></span><div class="cn-avatar-label-group__text"><span class="cn-table__primary">Daniel Costa</span><span class="cn-table__secondary">@daniel</span></div></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Active</span></td><td class="cn-table__cell">Engineering</td><td class="cn-table__cell">daniel@lumen.co</td><td class="cn-table__cell"><div style="display:flex;gap:var(--cn-space-1)"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Design</span><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">Product</span><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">+2</span></div></td><td class="cn-table__cell cn-table__actions"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Delete</span></button><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Edit</span></button></td></tr><tr class="cn-table__row"><td class="cn-table__cell cn-table__checkbox"><label class="cn-checkbox" data-size="md"><input class="cn-checkbox__input" type="checkbox" aria-label="Select Sofia Almeida"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-avatar-label-group" data-size="md"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" aria-hidden="true"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""></span><div class="cn-avatar-label-group__text"><span class="cn-table__primary">Sofia Almeida</span><span class="cn-table__secondary">@sofia</span></div></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Offline</span></td><td class="cn-table__cell">Design</td><td class="cn-table__cell">sofia@lumen.co</td><td class="cn-table__cell"><div style="display:flex;gap:var(--cn-space-1)"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Design</span><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">Product</span></div></td><td class="cn-table__cell cn-table__actions"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Delete</span></button><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Edit</span></button></td></tr><tr class="cn-table__row"><td class="cn-table__cell cn-table__checkbox"><label class="cn-checkbox" data-size="md"><input class="cn-checkbox__input" type="checkbox" aria-label="Select Lucas Ferreira"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-avatar-label-group" data-size="md"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" aria-hidden="true"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""></span><div class="cn-avatar-label-group__text"><span class="cn-table__primary">Lucas Ferreira</span><span class="cn-table__secondary">@lucas</span></div></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Active</span></td><td class="cn-table__cell">Frontend</td><td class="cn-table__cell">lucas@lumen.co</td><td class="cn-table__cell"><div style="display:flex;gap:var(--cn-space-1)"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Design</span><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">Product</span><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">+2</span></div></td><td class="cn-table__cell cn-table__actions"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Delete</span></button><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Edit</span></button></td></tr><tr class="cn-table__row"><td class="cn-table__cell cn-table__checkbox"><label class="cn-checkbox" data-size="md"><input class="cn-checkbox__input" type="checkbox" aria-label="Select Aisha Khan"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-avatar-label-group" data-size="md"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" aria-hidden="true"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""></span><div class="cn-avatar-label-group__text"><span class="cn-table__primary">Aisha Khan</span><span class="cn-table__secondary">@aisha</span></div></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Active</span></td><td class="cn-table__cell">Backend</td><td class="cn-table__cell">aisha@lumen.co</td><td class="cn-table__cell"><div style="display:flex;gap:var(--cn-space-1)"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Design</span><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">Product</span></div></td><td class="cn-table__cell cn-table__actions"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Delete</span></button><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Edit</span></button></td></tr></tbody></table></div>
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-table <table> — The <table>: width 100%, separate borders with zero spacing so radii and sticky headers work; 14px text in fg-muted. Add data-interactive when rows open something and data-sticky to pin the header inside a scroll container.
                                • .cn-table__caption <caption> · optional — Optional visible name of the table (14px medium fg-muted) above the header, left-aligned; prefer a CardHeader.
                                • .cn-table__head <thead> — Header row group: gray-50 fill, 44px tall (36 at compact), gray-200 hairline below.
                                • .cn-table__header-cell <th> — Column header: 12px medium fg-muted, 8 × 24px padding, no wrap, scope="col". Sortable headers wrap their text in a <button> and carry aria-sort; a help icon may follow the label.
                                • .cn-table__help <svg> · optional — 16px help-circle after a header label, fg-subtle, with a Tooltip explaining the column.
                                • .cn-table__body <tbody> — The data rows.
                                • .cn-table__row <tr> — One record, 64px (56 compact, 72 comfortable). data-selected (or aria-selected inside a grid) fills it gray-50; hover fills it gray-50 only when the table is data-interactive.
                                • .cn-table__cell <td> — A value: 14px fg-muted, 16 × 24px padding (12 × 20 compact), gray-200 hairline below, vertical-align middle. data-align="right" for numbers (tabular figures), "center" for icons.
                                • .cn-table__primary <span> · optional — The identifying text of a row (a name), 14px medium in ink; stack it over a secondary line inside an avatar label group.
                                • .cn-table__secondary <span> · optional — The supporting line under the primary text (handle, email), 14px fg-muted.
                                • .cn-table__checkbox <td> · optional — The 44px selection column (add this class alongside the cell / header-cell class): 24px left padding, 0 right, a Checkbox md; the next cell pulls its padding to 12px.
                                • .cn-table__footer <tfoot> · optional — Optional totals row group: gray-50 fill, hairline above, medium weight.
                                • .cn-table__sort <svg> · optional — 12px arrow after a sortable header's text (stroke 3). The unsorted chevron-selector shows on hover; the arrow shows when aria-sort is set and flips for ascending.
                                • .cn-table__actions <td> · optional — The last cell, right-aligned, holding one or two sm ghost / link Buttons or a ⋯ IconButton with a Menu. Add this class alongside the cell class (and on its header cell).

                                Props

                                propvalues (default outlined)meaning
                                densitycompact default comfortableRow height and cell padding: compact = 56px rows, 36px header, 12 × 20px cells (the reference sm); default = 64px rows, 44px header, 16 × 24px cells; comfortable = 72px rows for two-line avatar cells (the reference md).
                                variantdefault striped bordered carddefault = hairlines between rows only (inside a Card with padding none); striped = alternate rows gray-50, for wide tables with many columns; bordered = full grid lines, for numeric matrices; card = the table draws its own frame: radius 12, gray-200 ring, shadow-xs, for standalone tables on the canvas.

                                States

                                stateselectormeaning
                                rowHover[data-interactive] tbody tr:hoverOn a body row, not the root: gray-50 fill while hovered. Only when the root has data-interactive (rows open a record). (data-interactive on the <table>; native :hover on the row)
                                rowSelectedtr[data-selected], tr[aria-selected="true"]On a body row: gray-50 fill (the reference highlights selected rows like hovered ones); the checkbox shows the selection. (data-selected on the <tr> (aria-selected="true" inside role="grid"))
                                sortedth[aria-sort]On a header cell: the sort arrow is visible; ascending flips it. (aria-sort="ascending|descending" on the <th>)
                                sticky[data-sticky]Header cells stick to the top of the nearest scroll container. (data-sticky on the <table>, inside a container with overflow auto)

                                Rules

                                • Column headers are 12px medium, sentence case, 1–2 words, no trailing colon; never uppercase.
                                • Row heights come from density only: 56 / 64 / 72px; comfortable for cells with an avatar and two lines, compact for dense operational tables.
                                • The first column identifies the row: primary text in ink medium (name, ID) over an optional secondary line, inside an avatar label group when there is a picture. It is the widest and the only one that may truncate.
                                • Numbers are right-aligned with data-align="right" (tabular figures) and never truncated; text is left-aligned; center only icons and checkboxes.
                                • The selection column is the 44px checkbox cell with a Checkbox md; the header checkbox selects all. Selected rows fill gray-50, like hover.
                                • Row hover only when rows open something (data-interactive); the fill is gray-50 and never changes the row height.
                                • Actions live in the last column, right-aligned, as at most two sm ghost / link Buttons or one ⋯ IconButton with a Menu, visible at rest (never only on hover).
                                • Statuses in cells are Badge sm with a dot; people are the avatar label group; never an icon without text.
                                • One sorted column at a time, shown by aria-sort and the 12px arrow; the chevron-selector appears on hover for other sortable columns.
                                • Empty or loading tables replace the body with an EmptyState or Skeleton rows; never render a bare header.
                                • Use variant="card" for standalone tables on the canvas; inside a Card with padding="none" use the default variant so the card supplies the frame.

                                Accessibility

                                • A real <table> with <th scope="col"> in the header (and scope="row" on the identifying cell when useful). Never a grid of divs.
                                • Name the table with a CardHeader (aria-labelledby), a visible caption or aria-label.
                                • Sortable headers contain a <button>; aria-sort on the <th> reflects the current order. The button label names the column; announce the next order in a visually hidden hint if needed.
                                • Row checkboxes have aria-label "Select {name}"; the header checkbox "Select all". aria-selected on rows is valid only inside role="grid"; in plain tables use data-selected plus the checkbox.
                                • Interactive rows also need a focusable element per row (a link or button in the identifying cell); the row hover is not the only affordance.
                                • A scroll container with a sticky header gets tabindex="0" and an aria-label so keyboard users can scroll it.
                                Data display

                                List .cn-listedit

                                Stacked rows with a leading slot (avatar, icon, checkbox), a 14px medium ink title with an optional muted description, and a trailing slot (badge, meta, chevron). Gray-200 hairlines between rows, gray-50 hover and current rows; the same rhythm as a table row. Rows can be links or buttons.

                                Use: Collections where each item is one thing with a name and a little context: team members, conversations, settings entries, files, integrations. When people compare several columns of values, use Table. For key/value pairs use DescriptionList.

                                Team members with avatars and roles72px rows: Avatar md leading, ink medium name over the muted email, Badge sm trailing.
                                • Maya Chenmaya@lumen.co · Product ManagerAdmin
                                • Daniel Costadaniel@lumen.co · EngineeringEditor
                                • Sofia Almeidasofia@lumen.co · DesignEditor
                                • Lucas Ferreiralucas@lumen.co · FrontendInvited
                                HTML
                                <ul class="cn-list" data-density="default" data-variant="card" style="width:100%;max-width:560px"><li class="cn-list__item"><span class="cn-list__leading"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="online" aria-hidden="true"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""><span class="cn-avatar__status"></span></span></span><span class="cn-list__content"><span class="cn-list__title">Maya Chen</span><span class="cn-list__description">maya@lumen.co · Product Manager</span></span><span class="cn-list__trailing"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Admin</span></span></li><li class="cn-list__item"><span class="cn-list__leading"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="online" aria-hidden="true"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""><span class="cn-avatar__status"></span></span></span><span class="cn-list__content"><span class="cn-list__title">Daniel Costa</span><span class="cn-list__description">daniel@lumen.co · Engineering</span></span><span class="cn-list__trailing"><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">Editor</span></span></li><li class="cn-list__item"><span class="cn-list__leading"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="offline" aria-hidden="true"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""><span class="cn-avatar__status"></span></span></span><span class="cn-list__content"><span class="cn-list__title">Sofia Almeida</span><span class="cn-list__description">sofia@lumen.co · Design</span></span><span class="cn-list__trailing"><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">Editor</span></span></li><li class="cn-list__item"><span class="cn-list__leading"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" aria-hidden="true"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""></span></span><span class="cn-list__content"><span class="cn-list__title">Lucas Ferreira</span><span class="cn-list__description">lucas@lumen.co · Frontend</span></span><span class="cn-list__trailing"><span class="cn-badge" data-tone="warning" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Invited</span></span></li></ul>
                                Link items with chevron (compact)Whole row is the link; the current page is gray-50. Single-line 56px rows.
                                HTML
                                <nav class="cn-list" data-density="compact" data-variant="card" aria-label="Workspace settings" style="width:100%;max-width:560px"><a href="#" class="cn-list__item" aria-current="true"><span class="cn-list__content"><span class="cn-list__title">General</span></span><span class="cn-list__trailing">Lumen · Lisbon</span><svg class="cn-list__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></a><a href="#" class="cn-list__item"><span class="cn-list__content"><span class="cn-list__title">Members</span></span><span class="cn-list__trailing"><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">6</span></span><svg class="cn-list__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></a><a href="#" class="cn-list__item"><span class="cn-list__content"><span class="cn-list__title">Billing</span></span><span class="cn-list__trailing">Business plan</span><svg class="cn-list__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></a><a href="#" class="cn-list__item"><span class="cn-list__content"><span class="cn-list__title">Integrations</span></span><span class="cn-list__trailing"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>2 connected</span></span><svg class="cn-list__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></a></nav>
                                Inside a Card with a headerplain variant: the Card supplies the frame and the CardHeader names the list.

                                Integrations

                                Connect the tools your team already uses.

                                • SlackNotifications in #lumen-productConnected
                                • GitHubLink pull requests to tasksConnected
                                • FigmaEmbed designs in projects
                                HTML
                                <section class="cn-card" data-variant="default" data-padding="none" style="width:100%;max-width:560px"><header class="cn-card-header" data-variant="default" data-size="md"><div class="cn-card-header__content"><h2 class="cn-card-header__title">Integrations</h2><p class="cn-card-header__description">Connect the tools your team already uses.</p></div></header><ul class="cn-list" data-density="default" data-variant="plain"><li class="cn-list__item"><span class="cn-list__leading"><span class="cn-avatar" data-size="md" data-shape="square" data-tone="neutral" data-status="none" role="img" aria-label="Slack"><span class="cn-avatar__fallback" aria-hidden="true">SL</span></span></span><span class="cn-list__content"><span class="cn-list__title">Slack</span><span class="cn-list__description">Notifications in #lumen-product</span></span><span class="cn-list__trailing"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Connected</span></span></li><li class="cn-list__item"><span class="cn-list__leading"><span class="cn-avatar" data-size="md" data-shape="square" data-tone="neutral" data-status="none" role="img" aria-label="GitHub"><span class="cn-avatar__fallback" aria-hidden="true">GH</span></span></span><span class="cn-list__content"><span class="cn-list__title">GitHub</span><span class="cn-list__description">Link pull requests to tasks</span></span><span class="cn-list__trailing"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Connected</span></span></li><li class="cn-list__item"><span class="cn-list__leading"><span class="cn-avatar" data-size="md" data-shape="square" data-tone="neutral" data-status="none" role="img" aria-label="Figma"><span class="cn-avatar__fallback" aria-hidden="true">FG</span></span></span><span class="cn-list__content"><span class="cn-list__title">Figma</span><span class="cn-list__description">Embed designs in projects</span></span><span class="cn-list__trailing"><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Connect</span></button></span></li></ul></section>
                                Selected and disabled items
                                • Aisha Khanaisha@lumen.co · BackendOwner
                                • Noah Bergnoah@lumen.co · Product design2h ago
                                • Elena Rossielena@lumen.co · DeactivatedDeactivated
                                HTML
                                <ul class="cn-list" data-density="default" data-variant="card" style="width:100%;max-width:560px"><li class="cn-list__item" data-selected><span class="cn-list__leading"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="online" aria-hidden="true"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""><span class="cn-avatar__status"></span></span></span><span class="cn-list__content"><span class="cn-list__title">Aisha Khan</span><span class="cn-list__description">aisha@lumen.co · Backend</span></span><span class="cn-list__trailing"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Owner</span></span></li><li class="cn-list__item"><span class="cn-list__leading"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" aria-hidden="true"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""></span></span><span class="cn-list__content"><span class="cn-list__title">Noah Berg</span><span class="cn-list__description">noah@lumen.co · Product design</span></span><span class="cn-list__trailing"><span>2h ago</span></span></li><li class="cn-list__item" aria-disabled="true"><span class="cn-list__leading"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="none" aria-hidden="true"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""></span></span><span class="cn-list__content"><span class="cn-list__title">Elena Rossi</span><span class="cn-list__description">elena@lumen.co · Deactivated</span></span><span class="cn-list__trailing"><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">Deactivated</span></span></li></ul>
                                Button items (pick one)
                                HTML
                                <div class="cn-list" data-density="compact" data-variant="card" role="listbox" aria-label="Choose a sender" style="width:100%;max-width:420px"><button type="button" class="cn-list__item" role="option" aria-selected="true" data-selected><span class="cn-list__content"><span class="cn-list__title">hello@lumen.co</span><span class="cn-list__description">Default · verified domain</span></span><span class="cn-list__trailing"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></button><button type="button" class="cn-list__item" role="option" aria-selected="false"><span class="cn-list__content"><span class="cn-list__title">maya@lumen.co</span><span class="cn-list__description">Personal</span></span></button></div>

                                Matrix

                                variant ↓ / density →compactdefault
                                plain
                                • Maya Chenmaya@lumen.co · Product ManagerAdmin
                                • Daniel Costadaniel@lumen.co · EngineeringEditor
                                • Sofia Almeidasofia@lumen.co · DesignEditor
                                • Lucas Ferreiralucas@lumen.co · FrontendInvited
                                • Maya Chenmaya@lumen.co · Product ManagerAdmin
                                • Daniel Costadaniel@lumen.co · EngineeringEditor
                                • Sofia Almeidasofia@lumen.co · DesignEditor
                                • Lucas Ferreiralucas@lumen.co · FrontendInvited
                                card
                                • Maya Chenmaya@lumen.co · Product ManagerAdmin
                                • Daniel Costadaniel@lumen.co · EngineeringEditor
                                • Sofia Almeidasofia@lumen.co · DesignEditor
                                • Lucas Ferreiralucas@lumen.co · FrontendInvited
                                • Maya Chenmaya@lumen.co · Product ManagerAdmin
                                • Daniel Costadaniel@lumen.co · EngineeringEditor
                                • Sofia Almeidasofia@lumen.co · DesignEditor
                                • Lucas Ferreiralucas@lumen.co · FrontendInvited
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-list <ul> — Column of items. <ul> with <li> items for static lists, <nav>/<div> with <a>/<button> items for interactive lists. Items are direct children.
                                • .cn-list__item <li> — One row: flex, centered, 16 × 24px padding (12 × 20 compact), min height 72px with an avatar (56 compact). Also an <a> or <button> when the whole row is one action.
                                • .cn-list__leading <span> · optional — Optional slot before the content: Avatar md (sm at compact), a 20px icon or a Checkbox. Does not shrink.
                                • .cn-list__content <span> — Flexible middle: title over description, both truncating.
                                • .cn-list__title <span> — 14px medium in ink, one line, truncates with an ellipsis.
                                • .cn-list__description <span> · optional — 14px fg-muted under the title, one line, truncates.
                                • .cn-list__trailing <span> · optional — Optional slot after the content: Badge sm, Counter, a timestamp, a Switch or a sm ghost Button. Does not shrink.
                                • .cn-list__chevron <svg> · optional — 20px chevron-right in fg-subtle for items that navigate. Last child of link items.

                                Props

                                propvalues (default outlined)meaning
                                densitycompact defaultdefault = 72px min rows with 16 × 24px padding (two-line items with a 40px avatar); compact = 56px min rows with 12 × 20px padding (single-line items, settings, sm avatars).
                                variantplain cardplain = no frame, for lists inside a Card (padding none) or a drawer; card = the list draws its own frame (radius 12, gray-200 ring, shadow-xs) on a surface, for standalone lists on the canvas.

                                States

                                stateselectormeaning
                                hover > a:hover, & > button:hoverOn an interactive item (an <a> or <button> row), not the root: gray-50 fill. (make the item an <a href> or <button>; native :hover)
                                focus > a:focus-visible, & > button:focus-visibleOn an interactive item: inset 2px brand ring so it stays inside the frame. (native :focus-visible on the item)
                                current > [aria-current="true"], & > [data-selected]On an item: gray-50 fill for the current or selected row (the reference highlights like hover); the title stays ink. (aria-current="true" (navigation) or data-selected (selection) on the item)
                                disabled > [aria-disabled="true"], & > button:disabledOn an item: 50% opacity, no pointer events. (aria-disabled="true" on the item, or disabled on a button item)

                                Rules

                                • Every item has a title (14px medium ink); the description is optional, 14px muted and never longer than one line. Longer text belongs in the record, not the list.
                                • One leading slot and one trailing slot per item at most. Leading = identity (Avatar md, a 20px icon, a Checkbox); trailing = state or meta (Badge sm, Counter, timestamp, Switch, one sm Button).
                                • If the whole row is one action, the item is an <a> (navigate) or <button> (act) and shows the chevron only when it navigates to a page. Do not put buttons inside link rows.
                                • Rows never change height on hover; hover, current and selected are the same gray-50 fill.
                                • Use variant="card" only when the list stands alone on the canvas; inside a Card (padding none) or a drawer use plain so the Card supplies the frame.
                                • compact (56px, sm avatars) for single-line items and settings; default (72px, md avatars) when items have two lines. One density per list.
                                • Order by what the user is looking for (recency, priority), not alphabetically by default.
                                • Long lists paginate or virtualize after ~50 items; do not stack hundreds of rows.

                                Accessibility

                                • Static lists are <ul>/<li>; navigation lists are <nav> with <a href> items; pick-one lists use role="listbox" with role="option" and aria-selected on button items.
                                • aria-current="true" (or "page") marks the current navigation item; data-selected alone is only visual.
                                • Interactive rows are one focus stop each with the inset ring; the title text is the accessible name, so it must be meaningful on its own.
                                • Avatars in rows are decorative (aria-hidden or alt="") because the title names the person; presence must be in text when it matters.
                                • Truncated titles keep the full text in the DOM (ellipsis is CSS); add a title attribute only when the truncation hides essential information.
                                Data display

                                DescriptionList .cn-description-listedit

                                Key/value pairs: a 14px medium muted term and its ink value, side by side on a 160px column or stacked, divided by gray-200 hairlines. The quiet way to show the facts of one record.

                                Use: Details of one entity (a workspace, an invoice header, a customer profile) in drawers, cards and detail pages. For many records use Table; for a list of things use List.

                                Horizontal, divided160px term column, 14px medium muted terms, ink values, hairlines with 12px of padding.
                                Workspace
                                Lumen
                                Workspace ID
                                ws_8f2a91c4
                                Owner
                                Maya Chen
                                Plan
                                Business
                                Created
                                March 12, 2024
                                HTML
                                <dl class="cn-description-list" data-layout="horizontal" data-size="md" data-divided="yes" style="width:100%;max-width:560px"><div class="cn-description-list__item"><dt class="cn-description-list__term">Workspace</dt><dd class="cn-description-list__detail">Lumen</dd></div><div class="cn-description-list__item"><dt class="cn-description-list__term">Workspace ID</dt><dd class="cn-description-list__detail"><code class="cn-text-code-sm">ws_8f2a91c4</code></dd></div><div class="cn-description-list__item"><dt class="cn-description-list__term">Owner</dt><dd class="cn-description-list__detail">Maya Chen</dd></div><div class="cn-description-list__item"><dt class="cn-description-list__term">Plan</dt><dd class="cn-description-list__detail"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Business</span></dd></div><div class="cn-description-list__item"><dt class="cn-description-list__term">Created</dt><dd class="cn-description-list__detail">March 12, 2024</dd></div></dl>
                                Vertical (narrow columns)Term above the value, 4px apart.
                                Workspace
                                Lumen
                                Workspace ID
                                ws_8f2a91c4
                                Owner
                                Maya Chen
                                Plan
                                Business
                                Created
                                March 12, 2024
                                HTML
                                <dl class="cn-description-list" data-layout="vertical" data-size="md" data-divided="yes" style="width:100%;max-width:280px"><div class="cn-description-list__item"><dt class="cn-description-list__term">Workspace</dt><dd class="cn-description-list__detail">Lumen</dd></div><div class="cn-description-list__item"><dt class="cn-description-list__term">Workspace ID</dt><dd class="cn-description-list__detail"><code class="cn-text-code-sm">ws_8f2a91c4</code></dd></div><div class="cn-description-list__item"><dt class="cn-description-list__term">Owner</dt><dd class="cn-description-list__detail">Maya Chen</dd></div><div class="cn-description-list__item"><dt class="cn-description-list__term">Plan</dt><dd class="cn-description-list__detail"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Business</span></dd></div><div class="cn-description-list__item"><dt class="cn-description-list__term">Created</dt><dd class="cn-description-list__detail">March 12, 2024</dd></div></dl>
                                Compact inside a cardsize sm, no dividers: three facts as a quiet block.
                                Plan
                                Business · $2,900 / month
                                Seats
                                12 of 15 used
                                Renews
                                October 1, 2026
                                HTML
                                <section class="cn-card" data-variant="default" data-padding="sm" style="width:100%;max-width:360px"><div class="cn-card__body"><dl class="cn-description-list" data-layout="horizontal" data-size="sm" data-divided="no" style=""><div class="cn-description-list__item"><dt class="cn-description-list__term">Plan</dt><dd class="cn-description-list__detail">Business · $2,900 / month</dd></div><div class="cn-description-list__item"><dt class="cn-description-list__term">Seats</dt><dd class="cn-description-list__detail">12 of 15 used</dd></div><div class="cn-description-list__item"><dt class="cn-description-list__term">Renews</dt><dd class="cn-description-list__detail">October 1, 2026</dd></div></dl></div></section>
                                Values with components
                                Owner
                                Maya Chen
                                Status
                                Active
                                Website
                                lumen.co
                                Notes
                                Prefers email over Slack. Decision maker is the head of product, not the CEO.
                                HTML
                                <dl class="cn-description-list" data-layout="horizontal" data-size="md" data-divided="yes" style="width:100%;max-width:560px"><div class="cn-description-list__item"><dt class="cn-description-list__term">Owner</dt><dd class="cn-description-list__detail"><span class="cn-avatar-label-group" data-size="sm"><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span><span class="cn-avatar-label-group__text"><span class="cn-avatar-label-group__name">Maya Chen</span></span></span></dd></div><div class="cn-description-list__item"><dt class="cn-description-list__term">Status</dt><dd class="cn-description-list__detail"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Active</span></dd></div><div class="cn-description-list__item"><dt class="cn-description-list__term">Website</dt><dd class="cn-description-list__detail"><a href="#" class="cn-link">lumen.co</a></dd></div><div class="cn-description-list__item"><dt class="cn-description-list__term">Notes</dt><dd class="cn-description-list__detail">Prefers email over Slack. Decision maker is the head of product, not the CEO.</dd></div></dl>

                                Matrix

                                layout ↓ / size →smmd
                                horizontal
                                Workspace
                                Lumen
                                Workspace ID
                                ws_8f2a91c4
                                Owner
                                Maya Chen
                                Plan
                                Business
                                Created
                                March 12, 2024
                                Workspace
                                Lumen
                                Workspace ID
                                ws_8f2a91c4
                                Owner
                                Maya Chen
                                Plan
                                Business
                                Created
                                March 12, 2024
                                vertical
                                Workspace
                                Lumen
                                Workspace ID
                                ws_8f2a91c4
                                Owner
                                Maya Chen
                                Plan
                                Business
                                Created
                                March 12, 2024
                                Workspace
                                Lumen
                                Workspace ID
                                ws_8f2a91c4
                                Owner
                                Maya Chen
                                Plan
                                Business
                                Created
                                March 12, 2024
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-description-list <dl> — The <dl>. Column of items; the layout prop decides how each pair sits.
                                • .cn-description-list__item <div> — One pair. A <div> wrapping the dt and dd so they can be laid out as a row.
                                • .cn-description-list__term <dt> — The key. 14px medium fg-muted, sentence case, no colon.
                                • .cn-description-list__detail <dd> — The value. 14px in ink; may hold a Badge, a code value, a link or an Avatar with a name.

                                Props

                                propvalues (default outlined)meaning
                                layouthorizontal verticalhorizontal = term on a 160px column, value beside it (drawers, detail pages, ≥ 360px wide); vertical = term above the value, 4px apart (narrow columns, card grids, mobile).
                                sizesm mdmd = 14px term and value (default); sm = 12px, for dense cards and summaries.
                                dividedyes noyes = a gray-200 hairline between pairs with 12px of vertical padding (scannable lists of 4+ facts); no = pairs separated by an 8px gap only (2–3 facts inside a card).

                                Rules

                                • Terms are sentence-case nouns with no trailing colon ("Workspace ID", not "Workspace ID:"), 14px medium muted.
                                • Keep terms short (1–3 words) so they fit the 160px column; if a term needs a sentence, it is not a key/value pair.
                                • Values are facts in ink, not paragraphs. A value longer than two lines belongs in a section of its own.
                                • Empty values show an em dash ("—") in fg-subtle, never a blank cell and never the word "null" or "N/A".
                                • Order pairs by how people look things up (identity first, then status, then dates), not by the database schema.
                                • Use horizontal at ≥ 360px of width; below that, or in card grids, switch to vertical.
                                • divided="yes" for 4 or more pairs; "no" for 2–3 pairs inside a card that already has a frame.
                                • Values may hold one component (Badge sm, avatar label group, code, Link) but not buttons; actions live in the surface's header.

                                Accessibility

                                • Use the real <dl>/<dt>/<dd> elements; the wrapping <div> per pair is valid HTML and keeps the term/detail association.
                                • Do not put interactive controls in dd besides links; a control needs a labelled form field.
                                • Abbreviated terms (ID, VAT) get an <abbr title> or the expansion in the value on first use.
                                Data display

                                Accordion .cn-accordionedit

                                Stacked disclosure sections in the reference FAQ style: a full-width semibold trigger row with a chevron (or plus) that flips on the right, and muted body text that shows under it when open. Gray-200 hairlines between items; optional separated or card framing.

                                Use: Secondary content people open on demand: FAQ, advanced settings, details of a step, long explanations under a summary. Not for primary navigation (Tabs) and not for hiding required form fields.

                                Default (md), first item open16px semibold triggers on gray-200 hairlines, 20px chevron, 16px muted body.

                                Yes, you can try Lumen for free for 30 days. If you want, we'll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.

                                Of course. Our pricing scales with your company. Chat to our friendly team to find a solution that works for you.

                                We understand that things change. You can cancel your plan at any time and we'll refund you the difference already paid.
                                HTML
                                <div class="cn-accordion" data-variant="default" data-size="md" style="max-width:640px"><div class="cn-accordion__item" data-state="open"><h3 style="margin:0;font:inherit"><button type="button" class="cn-accordion__trigger" id="faq-a-1-trigger" aria-expanded="true" aria-controls="faq-a-1-content">Is there a free trial available?<svg class="cn-accordion__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></button></h3><div class="cn-accordion__content" id="faq-a-1-content" role="region" aria-labelledby="faq-a-1-trigger">Yes, you can try Lumen for free for 30 days. If you want, we'll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.</div></div><div class="cn-accordion__item" data-state="closed"><h3 style="margin:0;font:inherit"><button type="button" class="cn-accordion__trigger" id="faq-a-2-trigger" aria-expanded="false" aria-controls="faq-a-2-content">Can I change my plan later?<svg class="cn-accordion__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></button></h3><div class="cn-accordion__content" id="faq-a-2-content" role="region" aria-labelledby="faq-a-2-trigger">Of course. Our pricing scales with your company. Chat to our friendly team to find a solution that works for you.</div></div><div class="cn-accordion__item" data-state="closed"><h3 style="margin:0;font:inherit"><button type="button" class="cn-accordion__trigger" id="faq-a-3-trigger" aria-expanded="false" aria-controls="faq-a-3-content">What is your cancellation policy?<svg class="cn-accordion__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></button></h3><div class="cn-accordion__content" id="faq-a-3-content" role="region" aria-labelledby="faq-a-3-trigger">We understand that things change. You can cancel your plan at any time and we'll refund you the difference already paid.</div></div></div>
                                FAQ (lg) with plus / minus iconsThe reference FAQ section: 18px semibold, 24px padding, a 24px plus that becomes a minus when open.

                                Yes, you can try Lumen for free for 30 days. If you want, we'll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.

                                Of course. Our pricing scales with your company. Chat to our friendly team to find a solution that works for you.

                                We understand that things change. You can cancel your plan at any time and we'll refund you the difference already paid.
                                HTML
                                <div class="cn-accordion" data-variant="default" data-size="lg" style="max-width:768px"><div class="cn-accordion__item" data-state="open"><h3 style="margin:0;font:inherit"><button type="button" class="cn-accordion__trigger" id="faq-b-1-trigger" aria-expanded="true" aria-controls="faq-b-1-content">Is there a free trial available?<svg class="cn-accordion__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path class="cn-accordion__icon-v" d="M8 3v10"/><path d="M3 8h10"/></svg></button></h3><div class="cn-accordion__content" id="faq-b-1-content" role="region" aria-labelledby="faq-b-1-trigger">Yes, you can try Lumen for free for 30 days. If you want, we'll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.</div></div><div class="cn-accordion__item" data-state="closed"><h3 style="margin:0;font:inherit"><button type="button" class="cn-accordion__trigger" id="faq-b-2-trigger" aria-expanded="false" aria-controls="faq-b-2-content">Can I change my plan later?<svg class="cn-accordion__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path class="cn-accordion__icon-v" d="M8 3v10"/><path d="M3 8h10"/></svg></button></h3><div class="cn-accordion__content" id="faq-b-2-content" role="region" aria-labelledby="faq-b-2-trigger">Of course. Our pricing scales with your company. Chat to our friendly team to find a solution that works for you.</div></div><div class="cn-accordion__item" data-state="closed"><h3 style="margin:0;font:inherit"><button type="button" class="cn-accordion__trigger" id="faq-b-3-trigger" aria-expanded="false" aria-controls="faq-b-3-content">What is your cancellation policy?<svg class="cn-accordion__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path class="cn-accordion__icon-v" d="M8 3v10"/><path d="M3 8h10"/></svg></button></h3><div class="cn-accordion__content" id="faq-b-3-content" role="region" aria-labelledby="faq-b-3-trigger">We understand that things change. You can cancel your plan at any time and we'll refund you the difference already paid.</div></div></div>
                                SeparatedEach item is its own card (radius 12, gray-200 ring), 16px apart; use on the canvas.

                                Yes, you can try Lumen for free for 30 days. If you want, we'll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.

                                Of course. Our pricing scales with your company. Chat to our friendly team to find a solution that works for you.

                                We understand that things change. You can cancel your plan at any time and we'll refund you the difference already paid.
                                HTML
                                <div class="cn-accordion" data-variant="separated" data-size="md" style="max-width:640px"><div class="cn-accordion__item" data-state="open"><h3 style="margin:0;font:inherit"><button type="button" class="cn-accordion__trigger" id="faq-c-1-trigger" aria-expanded="true" aria-controls="faq-c-1-content">Is there a free trial available?<svg class="cn-accordion__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></button></h3><div class="cn-accordion__content" id="faq-c-1-content" role="region" aria-labelledby="faq-c-1-trigger">Yes, you can try Lumen for free for 30 days. If you want, we'll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.</div></div><div class="cn-accordion__item" data-state="closed"><h3 style="margin:0;font:inherit"><button type="button" class="cn-accordion__trigger" id="faq-c-2-trigger" aria-expanded="false" aria-controls="faq-c-2-content">Can I change my plan later?<svg class="cn-accordion__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></button></h3><div class="cn-accordion__content" id="faq-c-2-content" role="region" aria-labelledby="faq-c-2-trigger">Of course. Our pricing scales with your company. Chat to our friendly team to find a solution that works for you.</div></div><div class="cn-accordion__item" data-state="closed"><h3 style="margin:0;font:inherit"><button type="button" class="cn-accordion__trigger" id="faq-c-3-trigger" aria-expanded="false" aria-controls="faq-c-3-content">What is your cancellation policy?<svg class="cn-accordion__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></button></h3><div class="cn-accordion__content" id="faq-c-3-content" role="region" aria-labelledby="faq-c-3-trigger">We understand that things change. You can cancel your plan at any time and we'll refund you the difference already paid.</div></div></div>
                                Card, small, with a disabled itemOne card frame with 14px triggers; the disabled trigger stays readable.

                                Every outgoing message needs one approval. Replies to an approved thread can be auto-approved per conversation.

                                Messages go out between 08:00 and 18:00 in the recipient's time zone.

                                Upgrade to edit signatures per sender.
                                HTML
                                <div class="cn-accordion" data-variant="card" data-size="sm" style="max-width:560px"><div class="cn-accordion__item" data-state="open"><h3 style="margin:0;font:inherit"><button type="button" class="cn-accordion__trigger" id="faq-d-1-trigger" aria-expanded="true" aria-controls="faq-d-1-content">Approval rules<svg class="cn-accordion__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></button></h3><div class="cn-accordion__content" id="faq-d-1-content" role="region" aria-labelledby="faq-d-1-trigger">Every outgoing message needs one approval. Replies to an approved thread can be auto-approved per conversation.</div></div><div class="cn-accordion__item" data-state="closed"><h3 style="margin:0;font:inherit"><button type="button" class="cn-accordion__trigger" id="faq-d-2-trigger" aria-expanded="false" aria-controls="faq-d-2-content">Sending window<svg class="cn-accordion__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></button></h3><div class="cn-accordion__content" id="faq-d-2-content" role="region" aria-labelledby="faq-d-2-trigger">Messages go out between 08:00 and 18:00 in the recipient's time zone.</div></div><div class="cn-accordion__item" data-state="closed"><h3 style="margin:0;font:inherit"><button type="button" class="cn-accordion__trigger" id="faq-d-3-trigger" aria-expanded="false" aria-controls="faq-d-3-content" disabled>Custom signatures (Business plan)<svg class="cn-accordion__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></button></h3><div class="cn-accordion__content" id="faq-d-3-content" role="region" aria-labelledby="faq-d-3-trigger">Upgrade to edit signatures per sender.</div></div></div>

                                Matrix

                                variant ↓ / size →smmdlg
                                default

                                Yes, you can try Lumen for free for 30 days. If you want, we'll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.

                                Of course. Our pricing scales with your company. Chat to our friendly team to find a solution that works for you.

                                We understand that things change. You can cancel your plan at any time and we'll refund you the difference already paid.

                                Yes, you can try Lumen for free for 30 days. If you want, we'll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.

                                Of course. Our pricing scales with your company. Chat to our friendly team to find a solution that works for you.

                                We understand that things change. You can cancel your plan at any time and we'll refund you the difference already paid.

                                Yes, you can try Lumen for free for 30 days. If you want, we'll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.

                                Of course. Our pricing scales with your company. Chat to our friendly team to find a solution that works for you.

                                We understand that things change. You can cancel your plan at any time and we'll refund you the difference already paid.
                                separated

                                Yes, you can try Lumen for free for 30 days. If you want, we'll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.

                                Of course. Our pricing scales with your company. Chat to our friendly team to find a solution that works for you.

                                We understand that things change. You can cancel your plan at any time and we'll refund you the difference already paid.

                                Yes, you can try Lumen for free for 30 days. If you want, we'll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.

                                Of course. Our pricing scales with your company. Chat to our friendly team to find a solution that works for you.

                                We understand that things change. You can cancel your plan at any time and we'll refund you the difference already paid.

                                Yes, you can try Lumen for free for 30 days. If you want, we'll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.

                                Of course. Our pricing scales with your company. Chat to our friendly team to find a solution that works for you.

                                We understand that things change. You can cancel your plan at any time and we'll refund you the difference already paid.
                                card

                                Yes, you can try Lumen for free for 30 days. If you want, we'll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.

                                Of course. Our pricing scales with your company. Chat to our friendly team to find a solution that works for you.

                                We understand that things change. You can cancel your plan at any time and we'll refund you the difference already paid.

                                Yes, you can try Lumen for free for 30 days. If you want, we'll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.

                                Of course. Our pricing scales with your company. Chat to our friendly team to find a solution that works for you.

                                We understand that things change. You can cancel your plan at any time and we'll refund you the difference already paid.

                                Yes, you can try Lumen for free for 30 days. If you want, we'll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.

                                Of course. Our pricing scales with your company. Chat to our friendly team to find a solution that works for you.

                                We understand that things change. You can cancel your plan at any time and we'll refund you the difference already paid.
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-accordion <div> — Column of items.
                                • .cn-accordion__item <div> — One section. Carries data-state="open|closed", which drives the icon and hides the content. Hairline below.
                                • .cn-accordion__trigger <button> — Full-width <button aria-expanded aria-controls> inside a heading element. Semibold ink label left, icon right; padding 16 / 24px by size.
                                • .cn-accordion__icon <svg> — 20 / 24px chevron-down (rotates 180° when open) or plus (its vertical bar hides when open) at the end of the trigger, fg-subtle.
                                • .cn-accordion__icon-v <path> · optional — The vertical bar of the plus icon; hidden when the item is open so the plus becomes a minus.
                                • .cn-accordion__content <div> — The revealed panel: role="region", fg-muted body text, prose width, padding below. Hidden when the item is closed.

                                Props

                                propvalues (default outlined)meaning
                                variantdefault separated carddefault = items divided by hairlines, no frame (inside a Card or a page section); separated = each item is its own card (radius 12, ring) with a 16px gap (settings groups, FAQ on the canvas); card = the whole list in one card frame with items divided.
                                sizesm md lgsm = 14px semibold triggers with 12px padding and 14px body (dense settings); md = 16px semibold triggers, 16px padding, 16px body (the default in cards and drawers); lg = 18px semibold triggers, 24px padding, 16px body with a 24px icon (the reference FAQ section).

                                States

                                stateselectormeaning
                                open > [data-state="open"]On an item, not the root: the chevron is rotated (or the plus becomes a minus) and the content is visible. (data-state="open" on the item and aria-expanded="true" on its trigger)
                                closed > [data-state="closed"]On an item: the content is display none. (data-state="closed" on the item and aria-expanded="false" on its trigger)
                                hover > * > * > button:hoverOn the trigger: the icon darkens; no background change. (native :hover on the trigger)
                                focus > * > * > button:focus-visibleOn the trigger: the 4px brand ring. (native :focus-visible on the trigger)
                                disabled > * > * > button:disabledOn the trigger: 50% opacity, cannot open. (disabled on the trigger button)

                                Rules

                                • Every item carries data-state="open|closed" and its trigger aria-expanded; never leave the state implicit.
                                • Trigger labels are short noun phrases or questions in sentence case (≤ 10 words); no icons before the label, only the chevron or plus after it. One icon style per accordion.
                                • Content is prose or a small form, capped at prose width with a 40px right inset so it never runs under the icon; it is not a place for tables or lists of records.
                                • Default to all items closed except when there is one obvious first item; open at most one item by default.
                                • Allow several items open at once unless the content is mutually exclusive; do not force single-open behavior for FAQs.
                                • No hover background on the trigger; only the icon darkens. Focus ring on keyboard focus only.
                                • default inside cards and sections; separated on the canvas for long lists; card for a compact block of 2–4 items; lg only for FAQ sections on marketing and help pages.
                                • Do not nest accordions.

                                Accessibility

                                • The trigger is a <button> inside a heading (h2–h4 at the right level) with aria-expanded and aria-controls pointing to the content id.
                                • The content has role="region" and aria-labelledby pointing to the trigger id.
                                • Keyboard: Enter/Space toggle; Tab moves between triggers. Arrow-key navigation is optional and must not trap focus.
                                • Closed content is display none, so it is removed from the tab order and the accessibility tree; do not use visibility tricks that keep it focusable.
                                • Disabled triggers stay visible and readable; if the reason matters, say it in the label ("(Business plan)").
                                Data display

                                Timeline .cn-timelineedit

                                Events in order down a thin vertical line: an 8px dot (or a 32px avatar) per event, a 12px timestamp, a 14px semibold title and a muted note. The current event pulses in brand; completed events are green; failures are red.

                                Use: Activity of one thing over time: a release, a conversation, an order, a support ticket. For a multi-step process the user walks through use Stepper; for a list of records use List.

                                Release activity, current event lastThree completed events (green dots), one current (brand, pulsing). 12px time over a 14px semibold title.
                                1. Release branch createdv2.4.0 cut from main by Daniel Costa.
                                2. 12 pull requests merged8 features, 4 fixes.
                                3. Staging deploy approvedSofia Almeida signed off on the design review.
                                4. Production rollout in progress25% of workspaces migrated; no errors so far.
                                HTML
                                <ol class="cn-timeline" data-size="md" data-variant="default" style="max-width:480px"><li class="cn-timeline__item" data-state="complete"><span class="cn-timeline__marker" aria-hidden="true"></span><span class="cn-timeline__line" aria-hidden="true"></span><time class="cn-timeline__time" datetime="2026-09-11">Sep 8 · 09:12</time><span class="cn-timeline__title">Release branch created</span><span class="cn-timeline__description">v2.4.0 cut from main by Daniel Costa.</span></li><li class="cn-timeline__item" data-state="complete"><span class="cn-timeline__marker" aria-hidden="true"></span><span class="cn-timeline__line" aria-hidden="true"></span><time class="cn-timeline__time" datetime="2026-09-11">Sep 8 · 09:40</time><span class="cn-timeline__title">12 pull requests merged</span><span class="cn-timeline__description">8 features, 4 fixes.</span></li><li class="cn-timeline__item" data-state="complete"><span class="cn-timeline__marker" aria-hidden="true"></span><span class="cn-timeline__line" aria-hidden="true"></span><time class="cn-timeline__time" datetime="2026-09-11">Sep 9 · 14:05</time><span class="cn-timeline__title">Staging deploy approved</span><span class="cn-timeline__description">Sofia Almeida signed off on the design review.</span></li><li class="cn-timeline__item" data-state="current"><span class="cn-timeline__marker" aria-hidden="true"></span><span class="cn-timeline__line" aria-hidden="true"></span><time class="cn-timeline__time" datetime="2026-09-11">Sep 11 · 08:31</time><span class="cn-timeline__title">Production rollout in progress</span><span class="cn-timeline__description">25% of workspaces migrated; no errors so far.</span></li></ol>
                                Compact feed with a failureTime inline after the title; danger tone on the failure.
                                1. Rollout resumed25% of workspaces migrated.
                                2. Rollout pausedError rate above 1% in eu-west-1; Aisha Khan is investigating.
                                3. Staging deploy approved by Sofia Almeida
                                4. 12 pull requests merged
                                HTML
                                <ol class="cn-timeline" data-size="md" data-variant="compact" style="max-width:480px"><li class="cn-timeline__item" data-state="complete"><span class="cn-timeline__marker" aria-hidden="true"></span><span class="cn-timeline__line" aria-hidden="true"></span><time class="cn-timeline__time" datetime="2026-09-11">08:31</time><span class="cn-timeline__title">Rollout resumed</span><span class="cn-timeline__description">25% of workspaces migrated.</span></li><li class="cn-timeline__item" data-tone="danger"><span class="cn-timeline__marker" aria-hidden="true"></span><span class="cn-timeline__line" aria-hidden="true"></span><time class="cn-timeline__time" datetime="2026-09-11">08:02</time><span class="cn-timeline__title">Rollout paused</span><span class="cn-timeline__description">Error rate above 1% in eu-west-1; Aisha Khan is investigating.</span></li><li class="cn-timeline__item" data-state="complete"><span class="cn-timeline__marker" aria-hidden="true"></span><span class="cn-timeline__line" aria-hidden="true"></span><time class="cn-timeline__time" datetime="2026-09-11">Yesterday</time><span class="cn-timeline__title">Staging deploy approved by Sofia Almeida</span></li><li class="cn-timeline__item" data-state="complete"><span class="cn-timeline__marker" aria-hidden="true"></span><span class="cn-timeline__line" aria-hidden="true"></span><time class="cn-timeline__time" datetime="2026-09-11">Sep 9</time><span class="cn-timeline__title">12 pull requests merged</span></li></ol>
                                With avatars (activity by people)A 32px Avatar replaces the dot; the 2px line runs between avatars.
                                1. Maya Chen changed the due dateFrom Sep 12 to Sep 19.
                                2. Daniel Costa commented“Ready for review once the API is deployed.”
                                3. Sofia Almeida attached 2 filesBrand guidelines.pdf, Q3 roadmap.xlsx
                                HTML
                                <ol class="cn-timeline" data-size="md" data-variant="default" style="max-width:480px"><li class="cn-timeline__item"><span class="cn-timeline__avatar cn-avatar" data-size="sm" data-shape="circle" data-tone="neutral" data-status="none" aria-hidden="true"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""></span><span class="cn-timeline__line" aria-hidden="true"></span><time class="cn-timeline__time" datetime="2026-09-11">2h ago</time><span class="cn-timeline__title">Maya Chen changed the due date</span><span class="cn-timeline__description">From Sep 12 to Sep 19.</span></li><li class="cn-timeline__item"><span class="cn-timeline__avatar cn-avatar" data-size="sm" data-shape="circle" data-tone="neutral" data-status="none" aria-hidden="true"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""></span><span class="cn-timeline__line" aria-hidden="true"></span><time class="cn-timeline__time" datetime="2026-09-11">Yesterday</time><span class="cn-timeline__title">Daniel Costa commented</span><span class="cn-timeline__description">“Ready for review once the API is deployed.”</span></li><li class="cn-timeline__item"><span class="cn-timeline__avatar cn-avatar" data-size="sm" data-shape="circle" data-tone="neutral" data-status="none" aria-hidden="true"><img class="cn-avatar__image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""></span><span class="cn-timeline__line" aria-hidden="true"></span><time class="cn-timeline__time" datetime="2026-09-11">Sep 8</time><span class="cn-timeline__title">Sofia Almeida attached 2 files</span><span class="cn-timeline__description">Brand guidelines.pdf, Q3 roadmap.xlsx</span></li></ol>
                                Small, in a side panelNewest first is fine for feeds; be consistent per screen.
                                1. Production rollout in progress25% of workspaces migrated.
                                2. Staging deploy approved
                                3. Release branch created
                                HTML
                                <ol class="cn-timeline" data-size="sm" data-variant="default" style="max-width:320px"><li class="cn-timeline__item" data-state="current"><span class="cn-timeline__marker" aria-hidden="true"></span><span class="cn-timeline__line" aria-hidden="true"></span><time class="cn-timeline__time" datetime="2026-09-11">Sep 11</time><span class="cn-timeline__title">Production rollout in progress</span><span class="cn-timeline__description">25% of workspaces migrated.</span></li><li class="cn-timeline__item" data-state="complete"><span class="cn-timeline__marker" aria-hidden="true"></span><span class="cn-timeline__line" aria-hidden="true"></span><time class="cn-timeline__time" datetime="2026-09-11">Sep 9</time><span class="cn-timeline__title">Staging deploy approved</span></li><li class="cn-timeline__item" data-state="complete"><span class="cn-timeline__marker" aria-hidden="true"></span><span class="cn-timeline__line" aria-hidden="true"></span><time class="cn-timeline__time" datetime="2026-09-11">Sep 8</time><span class="cn-timeline__title">Release branch created</span></li></ol>

                                Matrix

                                variant ↓ / size →smmd
                                default
                                1. Release branch createdv2.4.0 cut from main by Daniel Costa.
                                2. 12 pull requests merged8 features, 4 fixes.
                                3. Staging deploy approvedSofia Almeida signed off on the design review.
                                4. Production rollout in progress25% of workspaces migrated; no errors so far.
                                1. Release branch createdv2.4.0 cut from main by Daniel Costa.
                                2. 12 pull requests merged8 features, 4 fixes.
                                3. Staging deploy approvedSofia Almeida signed off on the design review.
                                4. Production rollout in progress25% of workspaces migrated; no errors so far.
                                compact
                                1. Release branch createdv2.4.0 cut from main by Daniel Costa.
                                2. 12 pull requests merged8 features, 4 fixes.
                                3. Staging deploy approvedSofia Almeida signed off on the design review.
                                4. Production rollout in progress25% of workspaces migrated; no errors so far.
                                1. Release branch createdv2.4.0 cut from main by Daniel Costa.
                                2. 12 pull requests merged8 features, 4 fixes.
                                3. Staging deploy approvedSofia Almeida signed off on the design review.
                                4. Production rollout in progress25% of workspaces migrated; no errors so far.
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-timeline <ol> — Ordered list of events, oldest first (or newest first for activity feeds; be consistent per screen).
                                • .cn-timeline__item <li> — One event. Relative, left padding for the marker. data-state="complete|current" and data-tone="danger" color the marker.
                                • .cn-timeline__marker <span> — 8px dot on the line with a 1.5px surface ring. Gray-300 by default; green when complete, brand (pulsing) when current, red for failures.
                                • .cn-timeline__avatar <span> · optional — Optional 32px Avatar in place of the dot (an Avatar root that also carries this class) for activity by a person; the line runs behind it.
                                • .cn-timeline__line <span> — 2px gray-200 rule from this marker to the next. Hidden on the last item.
                                • .cn-timeline__time <time> — When it happened, 12px medium fg-subtle. Use a <time datetime>. Relative ("2h ago") or absolute, consistent per screen.
                                • .cn-timeline__title <span> — What happened, 14px semibold in ink, past tense ("Staging deploy approved").
                                • .cn-timeline__description <span> · optional — Optional one or two lines of detail, 14px fg-muted.

                                Props

                                propvalues (default outlined)meaning
                                sizesm mdmd = 14px titles with 20px between events (detail pages, drawers); sm = 12px titles and time, tighter spacing (side panels, cards).
                                variantdefault compactdefault = time above the title, each event a small block; compact = time inline after the title on one line, description under it, for dense activity feeds.

                                States

                                stateselectormeaning
                                complete > [data-state="complete"]On an item, not the root: the marker is green. (data-state="complete" on the item)
                                current > [data-state="current"]On an item: brand marker with a soft pulsing halo; the title is semibold brand. (data-state="current" on the item (at most one per timeline))
                                danger > [data-tone="danger"]On an item: red marker and title for a failure (bounce, rejection, error). (data-tone="danger" on the item)

                                Rules

                                • Titles are short past-tense facts ("Staging deploy approved", "12 pull requests merged"), sentence case, no trailing period.
                                • Timestamps use a <time datetime> element in 12px medium; pick relative or absolute per screen and keep it consistent across all events.
                                • At most one data-state="current" per timeline; it is the only pulsing element on the page.
                                • Marker colors mean state: gray = happened, green = completed step, brand = happening now, red = failed. Never other colors.
                                • Use the avatar marker when the event is something a person did; the dot when it is something the system did. One marker style per timeline.
                                • Descriptions are one or two lines of detail; anything longer (a message body) links to the record.
                                • Keep one direction per screen: oldest first for a process, newest first for an activity feed.
                                • Do not put buttons or badges inside events; the timeline reports, the surface acts. A link inside the description is fine.
                                • Long timelines collapse older events behind a "Show 12 earlier events" link Button; do not render hundreds of events.

                                Accessibility

                                • Use <ol> and <li> so the order and count are announced; the marker, avatar and line are aria-hidden decoration.
                                • State must be in the text, not only in the marker color: the title or description says "failed", "approved", "in progress".
                                • The pulse animation stops under prefers-reduced-motion (handled by the base stylesheet); the current item still stands out by color and weight.
                                • <time datetime> carries the machine-readable date so relative labels ("2h ago") remain meaningful.
                                Data display

                                Kanban .cn-kanbanedit

                                A board of columns with draggable cards. Columns are transparent with a header that names the stage, its count badge and who owns it; cards are small white cards (radius 12, gray-200 ring, shadow-xs) with a 14px medium title, a muted meta line and a footer of status Badge + Avatar.

                                Use: Work that moves through stages and is owned by someone at each stage: tasks, deals, tickets. When the stage does not matter, use a List; when people compare many fields, use a Table.

                                Board: in progress, waiting on you, blockedComponent previewEach column names its owner in a badge and shows its count. Cards carry a status Badge and the person responsible.

                                In progress

                                3Engineering

                                Migrate billing to usage-based plans

                                LUM-241 · Due Sep 19

                                Design the new onboarding checklist

                                LUM-233 · Due Sep 15

                                Rate-limit the public API

                                LUM-228 · Due Sep 22

                                Waiting on you

                                2Product

                                Approve pricing page copy

                                LUM-250 · Due today

                                Review Q3 roadmap

                                LUM-247 · Due Sep 12

                                Blocked

                                2Needs action

                                SSO login fails for Okta tenants

                                LUM-252 · Reported 2h ago

                                Invoice emails delayed

                                LUM-249 · Reported yesterday
                                HTML
                                <div class="cn-kanban" data-density="default" role="list" aria-label="Q3 launch board"><section class="cn-kanban__column" data-owner="agent" aria-label="In progress, 3 tasks"><header class="cn-kanban__column-header"><h3 class="cn-kanban__column-title">In progress</h3><span class="cn-kanban__column-count">3</span><span class="cn-kanban__column-owner">Engineering</span></header><div class="cn-kanban__column-body"><article class="cn-kanban__card" tabindex="0"><h4 class="cn-kanban__card-title">Migrate billing to usage-based plans</h4><div class="cn-kanban__card-meta">LUM-241 · Due Sep 19</div><div class="cn-kanban__card-footer"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>In progress</span><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span></span></div></article><article class="cn-kanban__card" tabindex="0"><h4 class="cn-kanban__card-title">Design the new onboarding checklist</h4><div class="cn-kanban__card-meta">LUM-233 · Due Sep 15</div><div class="cn-kanban__card-footer"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>In review</span><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Sofia Almeida"><span class="cn-avatar__fallback" aria-hidden="true">SA</span></span></div></article><article class="cn-kanban__card" tabindex="0"><h4 class="cn-kanban__card-title">Rate-limit the public API</h4><div class="cn-kanban__card-meta">LUM-228 · Due Sep 22</div><div class="cn-kanban__card-footer"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>In progress</span><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Lucas Ferreira"><span class="cn-avatar__fallback" aria-hidden="true">LF</span></span></div></article></div></section><section class="cn-kanban__column" data-owner="you" aria-label="Waiting on you, 2 tasks"><header class="cn-kanban__column-header"><h3 class="cn-kanban__column-title">Waiting on you</h3><span class="cn-kanban__column-count">2</span><span class="cn-kanban__column-owner">Product</span></header><div class="cn-kanban__column-body"><article class="cn-kanban__card" tabindex="0"><h4 class="cn-kanban__card-title">Approve pricing page copy</h4><div class="cn-kanban__card-meta">LUM-250 · Due today</div><div class="cn-kanban__card-footer"><span class="cn-badge" data-tone="warning" data-variant="soft" data-size="sm">Needs approval</span><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span></div></article><article class="cn-kanban__card" tabindex="0"><h4 class="cn-kanban__card-title">Review Q3 roadmap</h4><div class="cn-kanban__card-meta">LUM-247 · Due Sep 12</div><div class="cn-kanban__card-footer"><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">Review</span><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span></div></article></div></section><section class="cn-kanban__column" data-owner="action" aria-label="Blocked, 2 tasks"><header class="cn-kanban__column-header"><h3 class="cn-kanban__column-title">Blocked</h3><span class="cn-kanban__column-count">2</span><span class="cn-kanban__column-owner">Needs action</span></header><div class="cn-kanban__column-body"><article class="cn-kanban__card" tabindex="0"><h4 class="cn-kanban__card-title">SSO login fails for Okta tenants</h4><div class="cn-kanban__card-meta">LUM-252 · Reported 2h ago</div><div class="cn-kanban__card-footer"><span class="cn-badge" data-tone="danger" data-variant="soft" data-size="sm">Blocked</span><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span></span></div></article><article class="cn-kanban__card" tabindex="0"><h4 class="cn-kanban__card-title">Invoice emails delayed</h4><div class="cn-kanban__card-meta">LUM-249 · Reported yesterday</div><div class="cn-kanban__card-footer"><span class="cn-badge" data-tone="warning" data-variant="soft" data-size="sm">Overdue</span><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Lucas Ferreira"><span class="cn-avatar__fallback" aria-hidden="true">LF</span></span></div></article></div></section></div>
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-kanban <div> — Horizontal row of columns, 24px apart, scrolls sideways, columns aligned to the top.
                                • .cn-kanban__column <section> — One stage: 320px wide (288 compact), transparent. data-owner="agent|you|action" colors the owner badge.
                                • .cn-kanban__column-header <header> — Row with title, count and owner badge, 8px apart, 36px tall.
                                • .cn-kanban__column-title <h3> — Stage name, 14px semibold in ink.
                                • .cn-kanban__column-count <span> — Number of cards in a 22px gray badge (12px medium, gray-50, gray-200 ring).
                                • .cn-kanban__column-owner <span> — Owner badge pushed to the right (12px medium, radius 6): "Engineering", "Product", "Needs action". Color follows data-owner on the column.
                                • .cn-kanban__column-body <div> — Drop zone holding the cards 12px apart, min height 128px, radius 12. data-over shows a dashed gray-300 outline on gray-50 while a card hovers over it.
                                • .cn-kanban__card <article> — One item: white, gray-200 ring, shadow-xs, radius 12, 16px padding, grab cursor, focusable. data-state="dragging" lifts it.
                                • .cn-kanban__card-title <h4> — The thing's name, 14px medium in ink, up to two lines.
                                • .cn-kanban__card-meta <div> — One line of context, 14px fg-muted ("LUM-241 · Due Sep 19").
                                • .cn-kanban__card-footer <div> — Status (Badge sm) on the left, owner (Avatar xs) on the right, 8px above.

                                Props

                                propvalues (default outlined)meaning
                                densitycompact defaultdefault = 320px columns, 16px card padding, 12px between cards (boards with up to ~8 cards per column); compact = 288px columns, 12px card padding, 8px between cards, for boards with many cards.

                                States

                                stateselectormeaning
                                cardHover > * > * > article:hoverOn a card, not the root: the ring darkens to gray-300. (native :hover on the card)
                                cardFocus > * > * > article:focus-visibleOn a card: the 4px brand focus ring (cards are focusable for keyboard moves). (tabindex="0" on the card; native :focus-visible)
                                cardDragging > * > * > [data-state="dragging"]On a card while it is being dragged: shadow-lg, 1° tilt, grabbing cursor. (data-state="dragging" on the card)
                                columnOver > * > [data-over]On a column body while a card is dragged over it: dashed gray-300 border on a gray-50 fill. (data-over on the column body)

                                Rules

                                • Every column names an owner with data-owner: agent (a team is working on it), you (waiting for the user) or action (something went wrong or is overdue). The owner badge is the reader's first question answered.
                                • Column titles are stages or owners, 1–3 words; the count badge is always shown, even when zero.
                                • Cards show exactly: title (14px medium), one meta line, one status Badge sm and one Avatar xs. No descriptions, no buttons, no menus on the card; open the record for details.
                                • Card height comes from content only; do not force equal heights or add images.
                                • Columns are 320px (288px compact) and never stretch; the board scrolls sideways. Keep it to 3–6 columns.
                                • Drop feedback is the dashed column body (data-over) and the lifted card (data-state="dragging"); do not add insertion lines or ghost cards.
                                • Empty columns keep their min-height so there is somewhere to drop; show "Nothing here" in fg-subtle 14px when a column is empty for a while.
                                • Status tone follows the owner: brand (with dot) while a team works, warning when the user must act, danger for failures, neutral otherwise.

                                Accessibility

                                • The board is role="list" of columns; each column is a <section> with an aria-label that includes its title and count; each card is an <article> with a heading.
                                • Cards are focusable (tabindex="0"). Provide a keyboard way to move a card (a "Move to…" action in the record or a menu on Enter); drag and drop is never the only way.
                                • Announce moves with a polite live region ("Approve pricing page copy moved to In progress").
                                • Owner and status are text (the owner badge, the status badge), never only a color; the dot is reinforcement.
                                Data display

                                ChartFrame .cn-chart-frameedit

                                The card every chart lives in: an 18px semibold title and muted description on the left, a legend of round swatches on the right, a fixed-height body for the chart and a small footer line. White, gray-200 ring, shadow-xs, radius 12. The chart itself is your SVG or library output; the frame gives it the system's type, colors and spacing.

                                Use: Wrap every chart (bars, lines, areas, donuts) in dashboards and reports. Not for single numbers (Stat) and not for tables of values (Table).

                                Bars, two seriesSeries 1 = brand-600, series 2 = brand-300. Gridlines gray-100, axis text 12px gray-500, round 8px swatches.

                                Sign-ups and upgrades

                                Last 6 months · all plans

                                Sign-upsUpgrades
                                0204060Apr sign-ups: 42Apr upgrades: 12AprMay sign-ups: 55May upgrades: 19MayJun sign-ups: 38Jun upgrades: 15JunJul sign-ups: 60Jul upgrades: 24JulAug sign-ups: 47Aug upgrades: 18AugSep sign-ups: 51Sep upgrades: 21Sep
                                Source: product analytics · Updated 2h ago
                                HTML
                                <section class="cn-chart-frame" data-size="md" style="width:100%;max-width:560px"><header class="cn-chart-frame__header"><div><h3 class="cn-chart-frame__title">Sign-ups and upgrades</h3><p class="cn-chart-frame__description">Last 6 months · all plans</p></div><div class="cn-chart-frame__legend"><span class="cn-chart-frame__legend-item"><span class="cn-chart-frame__swatch" style="background:var(--cn-color-bg-action)"></span>Sign-ups</span><span class="cn-chart-frame__legend-item"><span class="cn-chart-frame__swatch" style="background:var(--cn-brand-300)"></span>Upgrades</span></div></header><div class="cn-chart-frame__body" data-scroll role="region" tabindex="0" aria-label="Sign-ups and upgrades chart; scroll horizontally on small screens"><svg viewBox="0 0 480 200" width="100%" role="img" aria-label="Sign-ups and upgrades per month, last 6 months. Upgrades grew from 12 to 21." style="display:block;max-width:100%"><line x1="32" x2="472" y1="170" y2="170" stroke="var(--cn-color-border-subtle)"/><text x="24" y="174" text-anchor="end">0</text><line x1="32" x2="472" y1="127" y2="127" stroke="var(--cn-color-border-subtle)"/><text x="24" y="131" text-anchor="end">20</text><line x1="32" x2="472" y1="83" y2="83" stroke="var(--cn-color-border-subtle)"/><text x="24" y="87" text-anchor="end">40</text><line x1="32" x2="472" y1="40" y2="40" stroke="var(--cn-color-border-subtle)"/><text x="24" y="44" text-anchor="end">60</text><rect x="46.5" y="79" width="20" height="91" rx="2" fill="var(--cn-color-bg-action)"><title>Apr sign-ups: 42</title></rect><rect x="70.5" y="144" width="20" height="26" rx="2" fill="var(--cn-brand-300)"><title>Apr upgrades: 12</title></rect><text x="68.5" y="188" text-anchor="middle">Apr</text><rect x="119.5" y="51" width="20" height="119" rx="2" fill="var(--cn-color-bg-action)"><title>May sign-ups: 55</title></rect><rect x="143.5" y="129" width="20" height="41" rx="2" fill="var(--cn-brand-300)"><title>May upgrades: 19</title></rect><text x="141.5" y="188" text-anchor="middle">May</text><rect x="192.5" y="88" width="20" height="82" rx="2" fill="var(--cn-color-bg-action)"><title>Jun sign-ups: 38</title></rect><rect x="216.5" y="137" width="20" height="33" rx="2" fill="var(--cn-brand-300)"><title>Jun upgrades: 15</title></rect><text x="214.5" y="188" text-anchor="middle">Jun</text><rect x="265.5" y="40" width="20" height="130" rx="2" fill="var(--cn-color-bg-action)"><title>Jul sign-ups: 60</title></rect><rect x="289.5" y="118" width="20" height="52" rx="2" fill="var(--cn-brand-300)"><title>Jul upgrades: 24</title></rect><text x="287.5" y="188" text-anchor="middle">Jul</text><rect x="338.5" y="68" width="20" height="102" rx="2" fill="var(--cn-color-bg-action)"><title>Aug sign-ups: 47</title></rect><rect x="362.5" y="131" width="20" height="39" rx="2" fill="var(--cn-brand-300)"><title>Aug upgrades: 18</title></rect><text x="360.5" y="188" text-anchor="middle">Aug</text><rect x="411.5" y="59" width="20" height="111" rx="2" fill="var(--cn-color-bg-action)"><title>Sep sign-ups: 51</title></rect><rect x="435.5" y="124" width="20" height="46" rx="2" fill="var(--cn-brand-300)"><title>Sep upgrades: 21</title></rect><text x="433.5" y="188" text-anchor="middle">Sep</text></svg></div><footer class="cn-chart-frame__footer">Source: product analytics · Updated 2h ago</footer></section>
                                Small sparkline with a StatA plain Stat in the header slot instead of a legend; the line is brand-600 with a faint fill and a hollow end dot.

                                Conversion rate

                                Last 30 days

                                4.2%
                                1.0 pt
                                HTML
                                <section class="cn-chart-frame" data-size="sm" style="width:100%;max-width:320px"><header class="cn-chart-frame__header"><div><h3 class="cn-chart-frame__title">Conversion rate</h3><p class="cn-chart-frame__description">Last 30 days</p></div><div class="cn-stat" data-size="sm" data-variant="plain" data-trend="up"><div class="cn-stat__value">4.2%</div><div class="cn-stat__delta"><svg class="cn-stat__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 13V3M4 7l4-4 4 4"/></svg><span>1.0 pt</span></div></div></header><div class="cn-chart-frame__body"><svg viewBox="0 0 320 80" width="100%" role="img" aria-label="Conversion rate, last 30 days: 3.2% to 4.2%" style="display:block;color:var(--cn-color-bg-action);max-width:100%"><defs><linearGradient id="cn-spark-fill" x1="0" x2="0" y1="0" y2="1"><stop offset="0" stop-color="currentColor" stop-opacity="0.16"/><stop offset="1" stop-color="currentColor" stop-opacity="0"/></linearGradient></defs><path d="M0,62 L32,58 L64,60 L96,48 L128,52 L160,40 L192,44 L224,30 L256,34 L288,22 L320,18 V80 H0 Z" fill="url(#cn-spark-fill)"/><polyline points="0,62 32,58 64,60 96,48 128,52 160,40 192,44 224,30 256,34 288,22 320,18" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round" stroke-linecap="round"/><circle cx="320" cy="18" r="4" fill="var(--cn-color-bg-surface)" stroke="currentColor" stroke-width="2"/></svg></div></section>
                                Large, no dataThe body hosts an EmptyState (with a modern FeaturedIcon) when there is nothing to draw.

                                Revenue by plan

                                This quarter

                                No revenue this quarter yet

                                Revenue appears here once the first invoice is paid.

                                HTML
                                <section class="cn-chart-frame" data-size="lg" style="width:100%;max-width:560px"><header class="cn-chart-frame__header"><div><h3 class="cn-chart-frame__title">Revenue by plan</h3><p class="cn-chart-frame__description">This quarter</p></div></header><div class="cn-chart-frame__body" style="display:flex;align-items:center;justify-content:center"><div class="cn-empty-state" data-size="sm" data-variant="default"><span class="cn-featured-icon cn-empty-state__icon" data-theme="modern" data-tone="gray" data-size="lg" data-shape="square"><svg class="cn-featured-icon__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg></span><h3 class="cn-empty-state__title">No revenue this quarter yet</h3><p class="cn-empty-state__description">Revenue appears here once the first invoice is paid.</p></div></div></section>

                                Matrix

                                size
                                sm

                                Sign-ups and upgrades

                                Last 6 months · all plans

                                Sign-upsUpgrades
                                0204060Apr sign-ups: 42Apr upgrades: 12AprMay sign-ups: 55May upgrades: 19MayJun sign-ups: 38Jun upgrades: 15JunJul sign-ups: 60Jul upgrades: 24JulAug sign-ups: 47Aug upgrades: 18AugSep sign-ups: 51Sep upgrades: 21Sep
                                Source: product analytics · Updated 2h ago
                                md

                                Sign-ups and upgrades

                                Last 6 months · all plans

                                Sign-upsUpgrades
                                0204060Apr sign-ups: 42Apr upgrades: 12AprMay sign-ups: 55May upgrades: 19MayJun sign-ups: 38Jun upgrades: 15JunJul sign-ups: 60Jul upgrades: 24JulAug sign-ups: 47Aug upgrades: 18AugSep sign-ups: 51Sep upgrades: 21Sep
                                Source: product analytics · Updated 2h ago
                                lg

                                Sign-ups and upgrades

                                Last 6 months · all plans

                                Sign-upsUpgrades
                                0204060Apr sign-ups: 42Apr upgrades: 12AprMay sign-ups: 55May upgrades: 19MayJun sign-ups: 38Jun upgrades: 15JunJul sign-ups: 60Jul upgrades: 24JulAug sign-ups: 47Aug upgrades: 18AugSep sign-ups: 51Sep upgrades: 21Sep
                                Source: product analytics · Updated 2h ago
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-chart-frame <section> — The card: white, gray-200 ring, shadow-xs, radius 12, 20 × 24px padding, column layout with 20px gaps.
                                • .cn-chart-frame__header <header> — Row: title + description on the left, legend (or a small control) on the right, 16px apart.
                                • .cn-chart-frame__title <h3> — What the chart shows, 18px semibold in ink, sentence case ("Sign-ups per month").
                                • .cn-chart-frame__description <p> · optional — Optional one line: period, filter or definition, 14px fg-muted.
                                • .cn-chart-frame__legend <div> · optional — Wrapping row of legend items, 12px apart, 14px fg-muted.
                                • .cn-chart-frame__legend-item <span> · optional — Swatch + series name, 8px apart.
                                • .cn-chart-frame__swatch <span> · optional — 8px round swatch with a 0.5px inner contrast ring, filled with the series color via an inline style using a token variable.
                                • .cn-chart-frame__body <div> — Where the chart goes. Position relative, min-height from size, the SVG fills the width; axis text is 12px fg-subtle. Also the place for an EmptyState or Skeleton.
                                • .cn-chart-frame__footer <footer> · optional — Optional source or note, 12px fg-subtle ("Source: product analytics · Updated 2h ago").

                                Props

                                propvalues (default outlined)meaning
                                sizesm md lgMinimum height of the body: sm 160px (sparklines and small multiples, 16 × 20px padding), md 240px (dashboard tiles), lg 320px (the main chart of a report page).

                                Rules

                                • Series colors in order: 1 = brand-600 (bg-action), 2 = brand-300, 3 = gray-400. Semantic colors (success, warning, danger, info) only when the series means that state. Maximum 6 series.
                                • Gridlines are gray-100 (border-subtle), axis text is 12px gray-500, no axis lines heavier than a hairline. No 3D, no gradients beyond a faint area fill, no shadows, bars with 2px radius.
                                • The title lives in the frame header, never inside the SVG; the SVG carries only marks, axes and labels.
                                • Every series in the legend (8px round swatches, 12px apart), every axis with units; never rely on color alone (order and labels carry the meaning).
                                • The chart fills the body width. Reflow axes and marks for narrow screens; for a static SVG whose labels would become too small, use data-scroll on the body with a named, focusable scroll region. Keep the minimum height from size.
                                • One chart per frame. Small multiples are several sm frames in a grid, not one frame with subplots.
                                • No data or loading: put an EmptyState (sm) or a Skeleton in the body; keep the header so the tile does not jump.
                                • Numbers on the chart use the same formatting as the rest of the page (locale, units).

                                Accessibility

                                • The SVG has role="img" and an aria-label that states the takeaway ("Upgrades grew from 12 to 21"), not just the chart name.
                                • Offer the data as a table (a "View as table" link Button in the footer or a visually hidden table) for screen readers and copy-paste.
                                • Tooltips on marks are enhancements; <title> elements inside the SVG give hover text without JS.
                                • Check series contrast against the actual surface in each supported theme. Use labels, patterns or outlines when a lighter series alone does not reach 3:1.
                                Data display

                                AccountCard .cn-account-cardedit

                                The signed-in person as a row: a 40px avatar with a presence dot, name in label-md, email in body-sm muted, and a ⋯ button that opens the account Menu. A hairline card at the bottom of the Sidebar, a plain row inside menus, or a compact row in account switchers.

                                Use: Use wherever the current account is shown with a way to act on it: the bottom of the Sidebar and the mobile drawer, the header of the account Menu, the rows of a workspace switcher. Not for other people in lists (use List with an Avatar) and not for a profile page header (PageHeader).

                                Card (sidebar)The bottom-of-sidebar card: 40px avatar with a presence dot, name, email, and the selector button that opens the account Menu.
                                HTML
                                <div style="width:100%;max-width:296px"><div class="cn-account-card" data-variant="card" data-size="md"><span class="cn-account-card__avatar" role="img" aria-label="Maya Chen, online">MC<span class="cn-account-card__status" data-status="online"></span></span><div class="cn-account-card__content"><span class="cn-account-card__name">Maya Chen</span><span class="cn-account-card__email">maya@lumen.co</span></div><button type="button" class="cn-account-card__menu" aria-label="Account menu" aria-haspopup="menu" aria-expanded="false"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 6.25l3-3 3 3M5 9.75l3 3 3-3"/></svg></button></div></div>
                                Plain (inside a menu or popover)No border or shadow, 8px padding; the surrounding panel provides the frame.
                                HTML
                                <div style="width:100%;max-width:296px"><div class="cn-account-card" data-variant="plain" data-size="md"><span class="cn-account-card__avatar" role="img" aria-label="Daniel Costa, away">DC<span class="cn-account-card__status" data-status="away"></span></span><div class="cn-account-card__content"><span class="cn-account-card__name">Daniel Costa</span><span class="cn-account-card__email">daniel@lumen.co</span></div></div></div>
                                Compact rows in a switcheraria-selected="true" draws the action-colored ring; the other rows are clickable (data-interactive).
                                HTML
                                <div style="width:100%;max-width:296px"><div role="listbox" aria-label="Switch account" style="display:flex;flex-direction:column;gap:var(--cn-space-1)"><div class="cn-account-card" data-variant="compact" data-size="md" role="option" aria-selected="true"><span class="cn-account-card__avatar" role="img" aria-label="Maya Chen, online">MC<span class="cn-account-card__status" data-status="online"></span></span><div class="cn-account-card__content"><span class="cn-account-card__name">Maya Chen</span><span class="cn-account-card__email">maya@lumen.co</span></div></div><div class="cn-account-card" data-variant="compact" data-size="md" role="option" aria-selected="false" data-interactive><span class="cn-account-card__avatar" role="img" aria-label="Lumen Design">LD</span><div class="cn-account-card__content"><span class="cn-account-card__name">Lumen Design</span><span class="cn-account-card__email">design@lumen.co</span></div></div><div class="cn-account-card" data-variant="compact" data-size="md" role="option" aria-selected="false" data-interactive><span class="cn-account-card__avatar" role="img" aria-label="Sofia Almeida, busy">SA<span class="cn-account-card__status" data-status="busy"></span></span><div class="cn-account-card__content"><span class="cn-account-card__name">Sofia Almeida</span><span class="cn-account-card__email">sofia@lumen.co</span></div></div></div></div>
                                Small size and presence valuessm = 32px avatar, label-sm name, body-xs email. online · away · busy · offline.
                                HTML
                                <div style="width:100%;max-width:296px"><div style="display:flex;flex-direction:column;gap:var(--cn-space-3)"><div class="cn-account-card" data-variant="card" data-size="sm"><span class="cn-account-card__avatar" role="img" aria-label="Aisha Khan, online">AK<span class="cn-account-card__status" data-status="online"></span></span><div class="cn-account-card__content"><span class="cn-account-card__name">Aisha Khan</span><span class="cn-account-card__email">aisha@lumen.co</span></div><button type="button" class="cn-account-card__menu" aria-label="Account menu" aria-haspopup="menu" aria-expanded="false"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 6.25l3-3 3 3M5 9.75l3 3 3-3"/></svg></button></div><div class="cn-account-card" data-variant="card" data-size="sm"><span class="cn-account-card__avatar" role="img" aria-label="Noah Berg, offline">NB<span class="cn-account-card__status" data-status="offline"></span></span><div class="cn-account-card__content"><span class="cn-account-card__name">Noah Berg</span><span class="cn-account-card__email">noah@lumen.co</span></div><button type="button" class="cn-account-card__menu" aria-label="Account menu" aria-haspopup="menu" aria-expanded="false"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 6.25l3-3 3 3M5 9.75l3 3 3-3"/></svg></button></div></div></div>

                                Recipes

                                With the account menu openThe Menu anchors to the card with .cn-menu-anchor + data-floating and spans its width. Log out is last, after a separator.
                                HTML
                                <div class="cn-menu-anchor" style="width:100%;max-width:296px;min-height:232px"><div class="cn-account-card" data-variant="card" data-size="md"><span class="cn-account-card__avatar" role="img" aria-label="Maya Chen, online">MC<span class="cn-account-card__status" data-status="online"></span></span><div class="cn-account-card__content"><span class="cn-account-card__name">Maya Chen</span><span class="cn-account-card__email">maya@lumen.co</span></div><button type="button" class="cn-account-card__menu" id="acct-menu-trigger" aria-label="Account menu" aria-haspopup="menu" aria-expanded="true" aria-controls="acct-menu"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 6.25l3-3 3 3M5 9.75l3 3 3-3"/></svg></button></div><div class="cn-menu" data-size="md" role="menu" id="acct-menu" aria-labelledby="acct-menu-trigger" data-state="open" data-floating style="width:100%"><button type="button" role="menuitem" class="cn-menu__item"><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><span class="cn-menu__text">View profile</span></button><button type="button" role="menuitem" class="cn-menu__item"><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg><span class="cn-menu__text">Settings</span></button><div class="cn-menu__separator" role="separator"></div><button type="button" role="menuitem" class="cn-menu__item"><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6.5 13.5h-3a1 1 0 01-1-1v-9a1 1 0 011-1h3M10.5 11l3-3-3-3M13.5 8h-7"/></svg><span class="cn-menu__text">Log out</span></button></div></div>

                                Matrix

                                variant ↓ / size →smmd
                                card
                                plain
                                compact
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-account-card <div> — The row: flex, 12px gap and padding, bg-surface, hairline, radius card, shadow-xs. Relative so a Menu can anchor to it.
                                • .cn-account-card__avatar <span> — 40px circle with initials or an <img>; relative so the status dot sits on its edge. role="img" + aria-label including presence.
                                • .cn-account-card__status <span> · optional — 10px presence dot at the bottom-right of the avatar with a 2px surface ring; data-status="online|away|busy|offline" colors it.
                                • .cn-account-card__content <div> — Name over email, fills the row, truncates.
                                • .cn-account-card__name <span> — label-md, one line, ellipsis.
                                • .cn-account-card__email <span> — body-sm muted, one line, ellipsis.
                                • .cn-account-card__menu <button> · optional — 32px ghost button with a 16px up/down selector icon that opens the account Menu. aria-label "Account menu", aria-haspopup, aria-expanded.

                                Props

                                propvalues (default outlined)meaning
                                variantcard plain compactcard = hairline card with shadow-xs, the Sidebar account card. plain = no border or shadow, 8px padding, for the header of a Menu or a Popover. compact = 8px padding, 32px avatar and smaller text for rows in an account switcher.
                                sizesm mdmd = 40px avatar, label-md name, body-sm email (sidebars, drawers). sm = 32px avatar, label-sm name, body-xs email for dense panels and popovers.

                                States

                                stateselectormeaning
                                hover[data-interactive]:hoverPointer over a clickable card (only when the whole row is the trigger, marked data-interactive): subtle fill. (data-interactive on the root and native :hover)
                                selected[aria-selected="true"], &[data-selected]The current account in a switcher: action-colored border and a 1px ring. (aria-selected="true" (inside a listbox) or data-selected on the root)
                                menuHover .cn-account-card__menu:hoverPointer over the ⋯ button (extraCss): subtle fill, ink icon. (native :hover on .cn-account-card__menu)
                                menuOpen .cn-account-card__menu[aria-expanded="true"]The account Menu is open (extraCss): the button keeps the subtle fill. (aria-expanded="true" on .cn-account-card__menu)
                                menuFocus .cn-account-card__menu:focus-visibleKeyboard focus on the ⋯ button shows the focus ring (extraCss). (native :focus-visible on .cn-account-card__menu)

                                Rules

                                • Shows the signed-in person only: their own name, their own email. Other people are List rows with an Avatar.
                                • One action: the selector button opens the account Menu (profile, settings, switch account, log out). Never add a second button to the row.
                                • Presence dots only when the product has real-time presence; otherwise omit the status part rather than showing everyone online.
                                • card in the Sidebar and the mobile drawer; plain as the header of the account Menu or a Popover; compact for the rows of an account switcher.
                                • Name and email truncate with an ellipsis; the full values are in the avatar's aria-label and the Menu header. Never wrap to two lines.
                                • A clickable whole row (data-interactive) is for switchers only; in the Sidebar the row is static and the button is the trigger.
                                • The selected account in a switcher gets the action-colored ring; hover uses the subtle fill. Never both on the same row.
                                • Keep the avatar neutral (initials on bg-subtle or the photo); the accent tone is reserved for the AI agent.

                                Accessibility

                                • The avatar carries role="img" and an aria-label with the full name and presence ("Maya Chen, online"); the dot alone is decorative.
                                • The menu button has aria-label "Account menu", aria-haspopup="menu", aria-expanded and aria-controls pointing at the Menu; Escape closes the menu and returns focus to it.
                                • In a switcher the rows are role="option" inside role="listbox" with aria-selected; Up/Down move, Enter selects.
                                • Name and email are plain text so they are read in order; do not hide them with CSS in narrow layouts, truncate instead.
                                • Contrast: name in fg-default, email in fg-muted (AA on bg-surface and bg-subtle).
                                Data display

                                ActivityFeed .cn-activity-feededit

                                A vertical feed of events on one object or workspace: a 32px avatar, "actor did action to target" in one line with a subtle timestamp, joined by a 2px connector. Events can carry a quoted comment, a file attachment, and the feed can end with a comment composer.

                                Use: Use on a record page (a deal, a project, a document) or a workspace home to show what people and agents did, newest first or oldest first per screen. For a process with known steps use Timeline; for chat use Message; for system notifications a person can act on use Notification.

                                SimpleOne sentence per event. The actor and the target are emphasized; the verb and the time are quiet.
                                MC
                                Maya Chencommented onQ3 launch plan
                                DC
                                Daniel Costauploadedbrand-guidelines-v3.pdf
                                SA
                                Sofia AlmeidaassignedHomepage redesign to Lucas Ferreira
                                AK
                                Aisha KhanmarkedInvoice INV-3066 as paid
                                HTML
                                <div class="cn-activity-feed" data-variant="simple" role="feed" aria-label="Activity" aria-busy="false" style="width:100%;max-width:560px"><article class="cn-activity-feed__item"><span class="cn-activity-feed__avatar" role="img" aria-label="Maya Chen">MC</span><span class="cn-activity-feed__line" aria-hidden="true"></span><div class="cn-activity-feed__content"><div class="cn-activity-feed__header"><span class="cn-activity-feed__actor">Maya Chen</span><span class="cn-activity-feed__action">commented on</span><span class="cn-activity-feed__target">Q3 launch plan</span><time class="cn-activity-feed__time" datetime="2026-09-11T07:40">2h ago</time></div></div></article><article class="cn-activity-feed__item"><span class="cn-activity-feed__avatar" role="img" aria-label="Daniel Costa">DC</span><span class="cn-activity-feed__line" aria-hidden="true"></span><div class="cn-activity-feed__content"><div class="cn-activity-feed__header"><span class="cn-activity-feed__actor">Daniel Costa</span><span class="cn-activity-feed__action">uploaded</span><span class="cn-activity-feed__target">brand-guidelines-v3.pdf</span><time class="cn-activity-feed__time" datetime="2026-09-11T05:12">4h ago</time></div></div></article><article class="cn-activity-feed__item"><span class="cn-activity-feed__avatar" role="img" aria-label="Sofia Almeida">SA</span><span class="cn-activity-feed__line" aria-hidden="true"></span><div class="cn-activity-feed__content"><div class="cn-activity-feed__header"><span class="cn-activity-feed__actor">Sofia Almeida</span><span class="cn-activity-feed__action">assigned</span><span class="cn-activity-feed__target">Homepage redesign to Lucas Ferreira</span><time class="cn-activity-feed__time" datetime="2026-09-10T16:05">Yesterday</time></div></div></article><article class="cn-activity-feed__item"><span class="cn-activity-feed__avatar" role="img" aria-label="Aisha Khan">AK</span><span class="cn-activity-feed__line" aria-hidden="true"></span><div class="cn-activity-feed__content"><div class="cn-activity-feed__header"><span class="cn-activity-feed__actor">Aisha Khan</span><span class="cn-activity-feed__action">marked</span><span class="cn-activity-feed__target">Invoice INV-3066 as paid</span><time class="cn-activity-feed__time" datetime="2026-09-09T11:30">Sep 9</time></div></div></article></div>
                                With comments and an attachmentQuoted comments sit in a subtle card; files are hairline rows that open the file.
                                MC
                                Maya Chencommented onQ3 launch plan
                                Can we move the pricing page to the second sprint? Legal still needs to review the new tiers.
                                NB
                                Noah BergapprovedQ3 launch plan
                                HTML
                                <div class="cn-activity-feed" data-variant="with-comments" role="feed" aria-label="Activity" aria-busy="false" style="width:100%;max-width:560px"><article class="cn-activity-feed__item"><span class="cn-activity-feed__avatar" role="img" aria-label="Maya Chen">MC</span><span class="cn-activity-feed__line" aria-hidden="true"></span><div class="cn-activity-feed__content"><div class="cn-activity-feed__header"><span class="cn-activity-feed__actor">Maya Chen</span><span class="cn-activity-feed__action">commented on</span><span class="cn-activity-feed__target">Q3 launch plan</span><time class="cn-activity-feed__time" datetime="2026-09-11T07:40">2h ago</time></div><div class="cn-activity-feed__comment">Can we move the pricing page to the second sprint? Legal still needs to review the new tiers.</div></div></article><article class="cn-activity-feed__item"><span class="cn-activity-feed__avatar" role="img" aria-label="Daniel Costa">DC</span><span class="cn-activity-feed__line" aria-hidden="true"></span><div class="cn-activity-feed__content"><div class="cn-activity-feed__header"><span class="cn-activity-feed__actor">Daniel Costa</span><span class="cn-activity-feed__action">replied</span><time class="cn-activity-feed__time" datetime="2026-09-11T08:32">1h ago</time></div><div class="cn-activity-feed__comment">Works for me. I attached the updated timeline.</div><a href="#" class="cn-activity-feed__attachment"><span class="cn-activity-feed__attachment-icon" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4.5 2.5h4.5l3 3v8a.5.5 0 01-.5.5h-7a.5.5 0 01-.5-.5v-11z"/><path d="M9 2.5v3h3"/></svg></span><span class="cn-activity-feed__attachment-name">q3-launch-timeline.xlsx</span><span class="cn-activity-feed__attachment-size">312 KB</span></a></div></article><article class="cn-activity-feed__item"><span class="cn-activity-feed__avatar" role="img" aria-label="Noah Berg">NB</span><span class="cn-activity-feed__line" aria-hidden="true"></span><div class="cn-activity-feed__content"><div class="cn-activity-feed__header"><span class="cn-activity-feed__actor">Noah Berg</span><span class="cn-activity-feed__action">approved</span><span class="cn-activity-feed__target">Q3 launch plan</span><time class="cn-activity-feed__time" datetime="2026-09-11T09:20">20m ago</time></div></div></article></div>
                                Compact (notification feed)24px avatars and 8px vertical padding for side panels and popovers.
                                EL
                                Elena Rossimentioned you inBrand refresh
                                TS
                                Tomás Silvarequested access toFinance dashboard
                                AK
                                Aisha KhanclosedTicket #4821
                                SA
                                Sofia AlmeidasharedOnboarding checklist
                                LF
                                Lucas FerreirajoinedLumen Design
                                HTML
                                <div class="cn-activity-feed" data-variant="compact" role="feed" aria-label="Activity" aria-busy="false" style="width:100%;max-width:420px"><article class="cn-activity-feed__item"><span class="cn-activity-feed__avatar" role="img" aria-label="Elena Rossi">EL</span><span class="cn-activity-feed__line" aria-hidden="true"></span><div class="cn-activity-feed__content"><div class="cn-activity-feed__header"><span class="cn-activity-feed__actor">Elena Rossi</span><span class="cn-activity-feed__action">mentioned you in</span><span class="cn-activity-feed__target">Brand refresh</span><time class="cn-activity-feed__time" datetime="2026-09-11T09:35">5m</time></div></div></article><article class="cn-activity-feed__item"><span class="cn-activity-feed__avatar" role="img" aria-label="Tomás Silva">TS</span><span class="cn-activity-feed__line" aria-hidden="true"></span><div class="cn-activity-feed__content"><div class="cn-activity-feed__header"><span class="cn-activity-feed__actor">Tomás Silva</span><span class="cn-activity-feed__action">requested access to</span><span class="cn-activity-feed__target">Finance dashboard</span><time class="cn-activity-feed__time" datetime="2026-09-11T09:08">32m</time></div></div></article><article class="cn-activity-feed__item"><span class="cn-activity-feed__avatar" role="img" aria-label="Aisha Khan">AK</span><span class="cn-activity-feed__line" aria-hidden="true"></span><div class="cn-activity-feed__content"><div class="cn-activity-feed__header"><span class="cn-activity-feed__actor">Aisha Khan</span><span class="cn-activity-feed__action">closed</span><span class="cn-activity-feed__target">Ticket #4821</span><time class="cn-activity-feed__time" datetime="2026-09-11T08:40">1h</time></div></div></article><article class="cn-activity-feed__item"><span class="cn-activity-feed__avatar" role="img" aria-label="Sofia Almeida">SA</span><span class="cn-activity-feed__line" aria-hidden="true"></span><div class="cn-activity-feed__content"><div class="cn-activity-feed__header"><span class="cn-activity-feed__actor">Sofia Almeida</span><span class="cn-activity-feed__action">shared</span><span class="cn-activity-feed__target">Onboarding checklist</span><time class="cn-activity-feed__time" datetime="2026-09-11T06:40">3h</time></div></div></article><article class="cn-activity-feed__item"><span class="cn-activity-feed__avatar" role="img" aria-label="Lucas Ferreira">LF</span><span class="cn-activity-feed__line" aria-hidden="true"></span><div class="cn-activity-feed__content"><div class="cn-activity-feed__header"><span class="cn-activity-feed__actor">Lucas Ferreira</span><span class="cn-activity-feed__action">joined</span><span class="cn-activity-feed__target">Lumen Design</span><time class="cn-activity-feed__time" datetime="2026-09-10T14:00">Yesterday</time></div></div></article></div>
                                With composerThe composer reuses the item layout so the connector runs into the current user's avatar.
                                MC
                                Maya Chencommented onPricing page copy
                                The second headline reads better. Can we A/B test both?
                                AK
                                Aisha Khanreplied
                                Yes — I set up the test for Monday.
                                LF
                                HTML
                                <div class="cn-activity-feed" data-variant="with-composer" role="feed" aria-label="Activity" aria-busy="false" style="width:100%;max-width:560px"><article class="cn-activity-feed__item"><span class="cn-activity-feed__avatar" role="img" aria-label="Maya Chen">MC</span><span class="cn-activity-feed__line" aria-hidden="true"></span><div class="cn-activity-feed__content"><div class="cn-activity-feed__header"><span class="cn-activity-feed__actor">Maya Chen</span><span class="cn-activity-feed__action">commented on</span><span class="cn-activity-feed__target">Pricing page copy</span><time class="cn-activity-feed__time" datetime="2026-09-11T08:40">1h ago</time></div><div class="cn-activity-feed__comment">The second headline reads better. Can we A/B test both?</div></div></article><article class="cn-activity-feed__item"><span class="cn-activity-feed__avatar" role="img" aria-label="Aisha Khan">AK</span><span class="cn-activity-feed__line" aria-hidden="true"></span><div class="cn-activity-feed__content"><div class="cn-activity-feed__header"><span class="cn-activity-feed__actor">Aisha Khan</span><span class="cn-activity-feed__action">replied</span><time class="cn-activity-feed__time" datetime="2026-09-11T09:00">40m ago</time></div><div class="cn-activity-feed__comment">Yes — I set up the test for Monday.</div></div></article><div class="cn-activity-feed__composer"><span class="cn-activity-feed__avatar" role="img" aria-label="Lucas Ferreira">LF</span><div class="cn-activity-feed__content"><div class="cn-textarea" data-size="sm" data-resize="none"><textarea class="cn-textarea__field" rows="2" placeholder="Leave a comment…" aria-label="Comment"></textarea></div><div class="cn-activity-feed__composer-actions"><button type="button" class="cn-button" data-variant="primary" data-size="sm"><span class="cn-button__label">Comment</span></button></div></div></div></div>

                                Matrix

                                variant
                                simple
                                MC
                                Maya Chencommented onQ3 launch plan
                                DC
                                Daniel Costauploadedbrand-guidelines-v3.pdf
                                SA
                                Sofia AlmeidaassignedHomepage redesign to Lucas Ferreira
                                AK
                                Aisha KhanmarkedInvoice INV-3066 as paid
                                with-comments
                                MC
                                Maya Chencommented onQ3 launch plan
                                DC
                                Daniel Costauploadedbrand-guidelines-v3.pdf
                                SA
                                Sofia AlmeidaassignedHomepage redesign to Lucas Ferreira
                                AK
                                Aisha KhanmarkedInvoice INV-3066 as paid
                                compact
                                MC
                                Maya Chencommented onQ3 launch plan
                                DC
                                Daniel Costauploadedbrand-guidelines-v3.pdf
                                SA
                                Sofia AlmeidaassignedHomepage redesign to Lucas Ferreira
                                AK
                                Aisha KhanmarkedInvoice INV-3066 as paid
                                with-composer
                                MC
                                Maya Chencommented onQ3 launch plan
                                DC
                                Daniel Costauploadedbrand-guidelines-v3.pdf
                                SA
                                Sofia AlmeidaassignedHomepage redesign to Lucas Ferreira
                                AK
                                Aisha KhanmarkedInvoice INV-3066 as paid
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-activity-feed <div> — The feed column (role="feed"). Flex column; width comes from the layout.
                                • .cn-activity-feed__item <article> — One event: avatar on the left, content on the right, relative so the connector can hang under the avatar. 12px vertical padding.
                                • .cn-activity-feed__avatar <span> — 32px circle with initials (24px in compact). role="img" + aria-label with the actor name, or a real Avatar image.
                                • .cn-activity-feed__line <span> — The 2px connector from this avatar to the next one, border-default. aria-hidden; hidden automatically on the last item.
                                • .cn-activity-feed__content <div> — Column next to the avatar: header line first, then optional comment, attachment or composer fields.
                                • .cn-activity-feed__header <div> — The one-line sentence: actor, action, target, time. Wraps on narrow widths.
                                • .cn-activity-feed__actor <span> — Who did it, label-sm semibold in fg-default ("Maya Chen").
                                • .cn-activity-feed__action <span> — The verb phrase in body-sm muted ("commented on", "uploaded").
                                • .cn-activity-feed__target <span> · optional — The object acted on, label-sm in fg-default ("Q3 launch plan"). Optional when the action is self-contained.
                                • .cn-activity-feed__time <time> — When, body-xs in fg-subtle, after the sentence. Always a <time datetime>.
                                • .cn-activity-feed__comment <div> · optional — Quoted comment text in a bg-subtle card (radius lg, 12px padding, body-sm).
                                • .cn-activity-feed__attachment <a> · optional — A file row: icon, name and size in a hairline card. A link to the file.
                                • .cn-activity-feed__attachment-icon <span> · optional — 32px bg-subtle square holding a 16px file icon.
                                • .cn-activity-feed__attachment-name <span> · optional — File name, label-sm, truncated.
                                • .cn-activity-feed__attachment-size <span> · optional — Size or type, body-xs fg-subtle ("2.4 MB").
                                • .cn-activity-feed__composer <div> · optional — The comment box at the end of the feed: the current user's avatar next to a Textarea and a Comment button.
                                • .cn-activity-feed__composer-actions <div> · optional — Right-aligned row under the textarea holding the primary sm "Comment" button.

                                Props

                                propvalues (default outlined)meaning
                                variantsimple with-comments compact with-composersimple = one sentence per event with a 32px avatar (record pages). with-comments = the same rows plus quoted comment cards and file attachments (discussion on a document). compact = 24px avatars, tighter rows, for a notification-style feed in a side panel or popover. with-composer = the feed ends with a Textarea + Comment button so people can reply in place.

                                States

                                stateselectormeaning
                                busy[aria-busy="true"]The feed is loading more events: content dims slightly. Set while fetching, clear when done. (aria-busy="true" on the root)
                                attachmentHover .cn-activity-feed__attachment:hoverPointer over a file row (lives on the attachment, styled in extraCss): subtle fill and a stronger border. (native :hover on .cn-activity-feed__attachment)
                                attachmentFocus .cn-activity-feed__attachment:focus-visibleKeyboard focus on a file row shows the focus ring (extraCss). (native :focus-visible on .cn-activity-feed__attachment)

                                Rules

                                • Every row is one sentence in the same shape: actor, verb, target, time. Actor and target are emphasized; the verb and the time stay quiet.
                                • Verbs are past tense and specific ("uploaded", "approved", "mentioned you in"); never "updated the record".
                                • Pick one order per screen: newest first for a home feed, oldest first on a record where the story matters.
                                • Timestamps are relative under 24h ("2h ago"), absolute after ("Sep 9"), always in a <time datetime>.
                                • Comments are quoted verbatim in the subtle card and truncated after four lines with a "Show more" link Button; the feed never renders an entire email.
                                • Attachments show name and size; the icon is the file type, not a thumbnail. One attachment row per file.
                                • Use compact only in side panels, popovers and notification drawers; on a page the default size keeps comments readable.
                                • The composer goes last, after the newest event, and uses a sm Textarea with a single primary "Comment" button; no toolbar.
                                • Long feeds paginate with a "Show 20 earlier events" link Button at the end, not infinite scroll inside a card.

                                Accessibility

                                • Root has role="feed" with an aria-label; each event is an <article> so screen readers can jump between them. Set aria-busy="true" on the root while loading more.
                                • Initials avatars carry role="img" and aria-label with the full name; the connector line is aria-hidden.
                                • The sentence must read correctly in DOM order: actor, action, target, time. Do not move the time before the actor with CSS.
                                • Attachments are real links with the file name as the accessible name; the size is extra text, not the only label.
                                • The composer textarea has a label ("Comment") and the button is a real <button>; Cmd/Ctrl+Enter may submit but is never the only way.
                                Data display

                                ActivityGauge .cn-activity-gaugeedit

                                Two or three concentric rings that each show a share of a goal, from the outer ring in the strongest brand tint to the inner one in the lightest, with a legend beside them that names each ring and its value.

                                Use: Use on a dashboard card to compare 2–3 related goals at a glance (sent / replied / booked; storage / bandwidth / seats). For one value use ProgressCircle; for more than three metrics or exact comparison use a bar chart in a ChartFrame; for a trend use a line chart.

                                Three metrics (md)Outer ring = first metric in brand.600, then brand.400, then brand.200. Offsets: 263.89 × 0.28 = 73.9, 201.06 × 0.52 = 104.6, 138.23 × 0.09 = 12.4.
                                HTML
                                <div class="cn-activity-gauge" data-size="md" role="img" aria-label="Outreach this week: emails sent 72%, replies 48%, meetings booked 91%"><svg class="cn-activity-gauge__rings" viewBox="0 0 100 100" aria-hidden="true"><circle class="cn-activity-gauge__ring" data-ring="1" data-track cx="50" cy="50" r="42"/><circle class="cn-activity-gauge__ring" data-ring="1" cx="50" cy="50" r="42" style="stroke-dasharray:263.89;stroke-dashoffset:73.9"/><circle class="cn-activity-gauge__ring" data-ring="2" data-track cx="50" cy="50" r="32"/><circle class="cn-activity-gauge__ring" data-ring="2" cx="50" cy="50" r="32" style="stroke-dasharray:201.06;stroke-dashoffset:104.6"/><circle class="cn-activity-gauge__ring" data-ring="3" data-track cx="50" cy="50" r="22"/><circle class="cn-activity-gauge__ring" data-ring="3" cx="50" cy="50" r="22" style="stroke-dasharray:138.23;stroke-dashoffset:12.4"/></svg><ul class="cn-activity-gauge__legend"><li class="cn-activity-gauge__legend-item"><span class="cn-activity-gauge__swatch" data-ring="1"></span><span class="cn-activity-gauge__label">Emails sent</span><span class="cn-activity-gauge__label" data-value>72%</span></li><li class="cn-activity-gauge__legend-item"><span class="cn-activity-gauge__swatch" data-ring="2"></span><span class="cn-activity-gauge__label">Replies</span><span class="cn-activity-gauge__label" data-value>48%</span></li><li class="cn-activity-gauge__legend-item"><span class="cn-activity-gauge__swatch" data-ring="3"></span><span class="cn-activity-gauge__label">Meetings booked</span><span class="cn-activity-gauge__label" data-value>91%</span></li></ul></div>
                                Two metricsWith two metrics only the outer two radii are used; the values can be counts.
                                HTML
                                <div class="cn-activity-gauge" data-size="md" role="img" aria-label="Usage: storage 64 GB of 100, bandwidth 37%"><svg class="cn-activity-gauge__rings" viewBox="0 0 100 100" aria-hidden="true"><circle class="cn-activity-gauge__ring" data-ring="1" data-track cx="50" cy="50" r="42"/><circle class="cn-activity-gauge__ring" data-ring="1" cx="50" cy="50" r="42" style="stroke-dasharray:263.89;stroke-dashoffset:95.0"/><circle class="cn-activity-gauge__ring" data-ring="2" data-track cx="50" cy="50" r="32"/><circle class="cn-activity-gauge__ring" data-ring="2" cx="50" cy="50" r="32" style="stroke-dasharray:201.06;stroke-dashoffset:126.7"/></svg><ul class="cn-activity-gauge__legend"><li class="cn-activity-gauge__legend-item"><span class="cn-activity-gauge__swatch" data-ring="1"></span><span class="cn-activity-gauge__label">Storage</span><span class="cn-activity-gauge__label" data-value>64 GB of 100</span></li><li class="cn-activity-gauge__legend-item"><span class="cn-activity-gauge__swatch" data-ring="2"></span><span class="cn-activity-gauge__label">Bandwidth</span><span class="cn-activity-gauge__label" data-value>37%</span></li></ul></div>
                                Sizes96px (sm) and 160px (lg) rings with body-xs / body-md legends.
                                HTML
                                <div style="display:flex;flex-wrap:wrap;align-items:center;gap:var(--cn-space-8)"><div class="cn-activity-gauge" data-size="sm" role="img" aria-label="Outreach: emails sent 72%, replies 48%, meetings booked 91%"><svg class="cn-activity-gauge__rings" viewBox="0 0 100 100" aria-hidden="true"><circle class="cn-activity-gauge__ring" data-ring="1" data-track cx="50" cy="50" r="42"/><circle class="cn-activity-gauge__ring" data-ring="1" cx="50" cy="50" r="42" style="stroke-dasharray:263.89;stroke-dashoffset:73.9"/><circle class="cn-activity-gauge__ring" data-ring="2" data-track cx="50" cy="50" r="32"/><circle class="cn-activity-gauge__ring" data-ring="2" cx="50" cy="50" r="32" style="stroke-dasharray:201.06;stroke-dashoffset:104.6"/><circle class="cn-activity-gauge__ring" data-ring="3" data-track cx="50" cy="50" r="22"/><circle class="cn-activity-gauge__ring" data-ring="3" cx="50" cy="50" r="22" style="stroke-dasharray:138.23;stroke-dashoffset:12.4"/></svg><ul class="cn-activity-gauge__legend"><li class="cn-activity-gauge__legend-item"><span class="cn-activity-gauge__swatch" data-ring="1"></span><span class="cn-activity-gauge__label">Emails sent</span><span class="cn-activity-gauge__label" data-value>72%</span></li><li class="cn-activity-gauge__legend-item"><span class="cn-activity-gauge__swatch" data-ring="2"></span><span class="cn-activity-gauge__label">Replies</span><span class="cn-activity-gauge__label" data-value>48%</span></li><li class="cn-activity-gauge__legend-item"><span class="cn-activity-gauge__swatch" data-ring="3"></span><span class="cn-activity-gauge__label">Meetings booked</span><span class="cn-activity-gauge__label" data-value>91%</span></li></ul></div><div class="cn-activity-gauge" data-size="lg" role="img" aria-label="Outreach: emails sent 72%, replies 48%, meetings booked 91%"><svg class="cn-activity-gauge__rings" viewBox="0 0 100 100" aria-hidden="true"><circle class="cn-activity-gauge__ring" data-ring="1" data-track cx="50" cy="50" r="42"/><circle class="cn-activity-gauge__ring" data-ring="1" cx="50" cy="50" r="42" style="stroke-dasharray:263.89;stroke-dashoffset:73.9"/><circle class="cn-activity-gauge__ring" data-ring="2" data-track cx="50" cy="50" r="32"/><circle class="cn-activity-gauge__ring" data-ring="2" cx="50" cy="50" r="32" style="stroke-dasharray:201.06;stroke-dashoffset:104.6"/><circle class="cn-activity-gauge__ring" data-ring="3" data-track cx="50" cy="50" r="22"/><circle class="cn-activity-gauge__ring" data-ring="3" cx="50" cy="50" r="22" style="stroke-dasharray:138.23;stroke-dashoffset:12.4"/></svg><ul class="cn-activity-gauge__legend"><li class="cn-activity-gauge__legend-item"><span class="cn-activity-gauge__swatch" data-ring="1"></span><span class="cn-activity-gauge__label">Emails sent</span><span class="cn-activity-gauge__label" data-value>72%</span></li><li class="cn-activity-gauge__legend-item"><span class="cn-activity-gauge__swatch" data-ring="2"></span><span class="cn-activity-gauge__label">Replies</span><span class="cn-activity-gauge__label" data-value>48%</span></li><li class="cn-activity-gauge__legend-item"><span class="cn-activity-gauge__swatch" data-ring="3"></span><span class="cn-activity-gauge__label">Meetings booked</span><span class="cn-activity-gauge__label" data-value>91%</span></li></ul></div></div>

                                Recipes

                                Dashboard cardTitle names the goal, description the period; the gauge is the whole body.

                                Outreach this week

                                Share of the weekly goal, Sep 7–11

                                HTML
                                <section class="cn-card" data-variant="default" data-padding="md" style="max-width:420px"><header class="cn-card__header"><div><h3 class="cn-card__title">Outreach this week</h3><p class="cn-card__description">Share of the weekly goal, Sep 7–11</p></div></header><div class="cn-card__body"><div class="cn-activity-gauge" data-size="md" role="img" aria-label="Outreach this week: emails sent 72%, replies 48%, meetings booked 91%"><svg class="cn-activity-gauge__rings" viewBox="0 0 100 100" aria-hidden="true"><circle class="cn-activity-gauge__ring" data-ring="1" data-track cx="50" cy="50" r="42"/><circle class="cn-activity-gauge__ring" data-ring="1" cx="50" cy="50" r="42" style="stroke-dasharray:263.89;stroke-dashoffset:73.9"/><circle class="cn-activity-gauge__ring" data-ring="2" data-track cx="50" cy="50" r="32"/><circle class="cn-activity-gauge__ring" data-ring="2" cx="50" cy="50" r="32" style="stroke-dasharray:201.06;stroke-dashoffset:104.6"/><circle class="cn-activity-gauge__ring" data-ring="3" data-track cx="50" cy="50" r="22"/><circle class="cn-activity-gauge__ring" data-ring="3" cx="50" cy="50" r="22" style="stroke-dasharray:138.23;stroke-dashoffset:12.4"/></svg><ul class="cn-activity-gauge__legend"><li class="cn-activity-gauge__legend-item"><span class="cn-activity-gauge__swatch" data-ring="1"></span><span class="cn-activity-gauge__label">Emails sent</span><span class="cn-activity-gauge__label" data-value>72%</span></li><li class="cn-activity-gauge__legend-item"><span class="cn-activity-gauge__swatch" data-ring="2"></span><span class="cn-activity-gauge__label">Replies</span><span class="cn-activity-gauge__label" data-value>48%</span></li><li class="cn-activity-gauge__legend-item"><span class="cn-activity-gauge__swatch" data-ring="3"></span><span class="cn-activity-gauge__label">Meetings booked</span><span class="cn-activity-gauge__label" data-value>91%</span></li></ul></div></div></section>

                                Matrix

                                size
                                sm
                                md
                                lg
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-activity-gauge <div> — Row (inline-flex, align center) with the rings on the left and the legend on the right. role="img" with an aria-label that reads every metric. Carries data-size.
                                • .cn-activity-gauge__rings <svg> — The square svg, viewBox 0 0 100 100, holding a track and a value ring per metric.
                                • .cn-activity-gauge__ring <circle> — One ring. data-ring="1|2|3" sets the color (brand.600 / 400 / 200, outside in); data-track marks the grey background ring. Value rings set stroke-dasharray (circumference) and stroke-dashoffset inline.
                                • .cn-activity-gauge__legend <ul> — Stacked list of metrics next to the rings, gap space.2, no bullets.
                                • .cn-activity-gauge__legend-item <li> — One row: swatch, label, value (label with data-value).
                                • .cn-activity-gauge__swatch <span> — 8px round dot in the ring color (data-ring).
                                • .cn-activity-gauge__label <span> — body-sm text: the metric name in fg-muted; with data-value, the number pushed to the end in fg-default, tabular.

                                Props

                                propvalues (default outlined)meaning
                                sizesm md lgRing box 96 / 128 / 160px. sm for a stat tile, md for a dashboard card, lg for a page section with a heading.

                                Rules

                                • Two or three rings, never one (ProgressCircle) or four. Order metrics by importance: the first is the outer ring in the strongest tint.
                                • Geometry is fixed: viewBox 0 0 100 100, stroke 8, radii 42 / 32 / 22 with circumferences 263.89 / 201.06 / 138.23. Each value ring sets stroke-dasharray to its circumference and stroke-dashoffset = circumference × (1 − value / 100) inline.
                                • Every value ring has a track ring of the same radius behind it (data-track) so an empty metric still reads as a ring.
                                • Colors are the three brand tints only; the rings compare shares of goals, not statuses. Use a bar chart when the metrics need distinct hues.
                                • The legend is mandatory and lists the metrics in ring order, each with its value; the number may be a count ("64 GB of 100") when the percentage alone is unclear.
                                • Values are capped at 100%; show "100%" and say the goal was exceeded in the caption or the card description.
                                • One gauge per card. sm in stat tiles, md in dashboard cards, lg in a page section.

                                Accessibility

                                • The root is role="img" with an aria-label that reads every metric and value in ring order; the svg is aria-hidden.
                                • The legend repeats the same information as visible text, so the rings are never the only source.
                                • Swatches are decorative; the pairing between ring and legend is also carried by order (outer to inner, top to bottom).
                                Data display

                                BadgeGroup .cn-badge-groupedit

                                A pill that holds a small inner badge plus a message and an arrow: the "badge group" used for announcements above hero titles ("New feature · Check out the team dashboard →").

                                Use: Announcements, release notes and "what is new" links above a page or hero title. One per page. Not for status in tables (use Badge).

                                Light, leading (default)
                                HTML
                                <a href="#" class="cn-badge-group" data-tone="brand" data-theme="light" data-size="md" data-align="leading"><span class="cn-badge-group__badge">New feature</span><span class="cn-badge-group__label">Check out the team dashboard</span><svg class="cn-badge-group__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg></a>
                                Modern
                                HTML
                                <a href="#" class="cn-badge-group" data-tone="brand" data-theme="modern" data-size="md" data-align="leading"><span class="cn-badge-group__badge">We're hiring!</span><span class="cn-badge-group__label">Join our remote team</span><svg class="cn-badge-group__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg></a>
                                Tones
                                HTML
                                <a href="#" class="cn-badge-group" data-tone="gray" data-theme="light" data-size="md" data-align="leading"><span class="cn-badge-group__badge">Update</span><span class="cn-badge-group__label">Version 4.2 is live</span><svg class="cn-badge-group__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg></a> <a href="#" class="cn-badge-group" data-tone="success" data-theme="light" data-size="md" data-align="leading"><span class="cn-badge-group__badge">Resolved</span><span class="cn-badge-group__label">All systems normal</span><svg class="cn-badge-group__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg></a> <a href="#" class="cn-badge-group" data-tone="error" data-theme="light" data-size="md" data-align="leading"><span class="cn-badge-group__badge">Incident</span><span class="cn-badge-group__label">API latency degraded</span><svg class="cn-badge-group__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg></a>
                                Trailing and sizes
                                HTML
                                <a href="#" class="cn-badge-group" data-tone="brand" data-theme="light" data-size="sm" data-align="trailing"><span class="cn-badge-group__badge">New</span><span class="cn-badge-group__label">Read the changelog</span><svg class="cn-badge-group__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg></a> <a href="#" class="cn-badge-group" data-tone="brand" data-theme="light" data-size="lg" data-align="leading"><span class="cn-badge-group__badge">New feature</span><span class="cn-badge-group__label">Check out the team dashboard</span><svg class="cn-badge-group__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg></a>

                                Matrix

                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-badge-group <a> — The outer pill. A link when it navigates.
                                • .cn-badge-group__badge <span> — The inner pill (e.g. "New feature").
                                • .cn-badge-group__label <span> — The message.
                                • .cn-badge-group__icon <svg> — Trailing arrow, 16px.

                                Props

                                propvalues (default outlined)meaning
                                tonebrand gray success warning errorColor family of the group.
                                themelight modernlight = tinted outer pill with white inner badge; modern = white outer pill with grey ring and a tinted inner badge.
                                sizesm md lgOuter pill 24 / 28 / 32px; text 12 / 14 / 14.
                                alignleading trailingleading = inner badge first; trailing = inner badge after the message.

                                States

                                stateselectormeaning
                                hover:hoverSlightly stronger fill; arrow moves 2px. (native)
                                focus:focus-visibleFocus ring. (native)

                                Rules

                                • One badge group per page, placed above the hero or page title with 16–24px below it.
                                • Inner badge: 1–2 words; label: one short sentence; always ends with the arrow when it links.
                                • the system equivalent: badge group (light / modern; leading / trailing; sm–lg).

                                Accessibility

                                • When it navigates it is an <a href>; otherwise a <span> without hover affordance.
                                • The inner badge text is part of the link name; do not hide it.
                                Data display

                                Message .cn-messageedit

                                Chat messages in a thread: incoming bubbles on the left in bg-subtle, outgoing on the right in the action color, each with a name and time line, a 32px avatar, optional file or image attachments, a three-dot typing indicator, centered date chips and a composer with a toolbar and a primary Send.

                                Use: Use for two-way conversations in real time: support chat, a thread with a teammate, the transcript of an agent conversation. For a comment stream on a document use ActivityFeed; for a timeline of events use Timeline; for one-off notifications use Notification.

                                Thread with an attachment and typingReceived on the left with an avatar, sent on the right in the action color. The typing indicator is an incoming bubble with three bouncing dots.
                                MC
                                Maya Chen
                                Morning! Did the Q3 launch plan land in your inbox?
                                You
                                It did — reading it now. Two questions about the pricing page.
                                MC
                                Maya Chen
                                Here is the latest deck, the pricing slides start on page 12.q3-launch-plan-v4.pdfPDF · 2.4 MB
                                You
                                Perfect, thanks. I will leave comments before lunch.
                                MC
                                HTML
                                <div class="cn-message" data-variant="thread" data-density="default" role="log" aria-label="Conversation with Maya Chen" style="width:100%;max-width:560px"><div class="cn-message__date-divider" role="separator">Today</div><div class="cn-message__item" data-direction="in"><span class="cn-message__avatar" role="img" aria-label="Maya Chen">MC</span><div class="cn-message__content"><div class="cn-message__meta"><span class="cn-message__name">Maya Chen</span><time class="cn-message__time" datetime="2026-09-11T09:12">09:12</time></div><div class="cn-message__bubble">Morning! Did the Q3 launch plan land in your inbox?</div></div></div><div class="cn-message__item" data-direction="out"><div class="cn-message__content"><div class="cn-message__meta"><span class="cn-message__name">You</span><time class="cn-message__time" datetime="2026-09-11T09:14">09:14</time></div><div class="cn-message__bubble">It did — reading it now. Two questions about the pricing page.</div></div></div><div class="cn-message__item" data-direction="in"><span class="cn-message__avatar" role="img" aria-label="Maya Chen">MC</span><div class="cn-message__content"><div class="cn-message__meta"><span class="cn-message__name">Maya Chen</span><time class="cn-message__time" datetime="2026-09-11T09:16">09:16</time></div><div class="cn-message__bubble">Here is the latest deck, the pricing slides start on page 12.<a href="#" class="cn-message__attachment"><span class="cn-message__attachment-icon" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4.5 2.5h4.5l3 3v8a.5.5 0 01-.5.5h-7a.5.5 0 01-.5-.5v-11z"/><path d="M9 2.5v3h3"/></svg></span><span><span class="cn-message__attachment-name">q3-launch-plan-v4.pdf</span><span class="cn-message__attachment-meta">PDF · 2.4 MB</span></span></a></div></div></div><div class="cn-message__item" data-direction="out"><div class="cn-message__content"><div class="cn-message__meta"><span class="cn-message__name">You</span><time class="cn-message__time" datetime="2026-09-11T09:20">09:20</time></div><div class="cn-message__bubble">Perfect, thanks. I will leave comments before lunch.</div></div></div><div class="cn-message__item" data-direction="in"><span class="cn-message__avatar" role="img" aria-label="Maya Chen">MC</span><div class="cn-message__content"><div class="cn-message__typing" role="status" aria-label="Maya Chen is typing"><span class="cn-message__dot"></span><span class="cn-message__dot"></span><span class="cn-message__dot"></span></div></div></div></div>
                                ComposerAttach, emoji and voice as ghost IconButtons; Send is the only primary button. Enter sends, Shift+Enter adds a line.
                                HTML
                                <div class="cn-message" data-variant="composer" data-density="default" role="log" aria-label="Conversation with Maya Chen" style="width:100%;max-width:560px"><div class="cn-message__composer"><textarea class="cn-message__composer-input" rows="2" placeholder="Message Maya Chen…" aria-label="Message"></textarea><div class="cn-message__composer-toolbar"><div class="cn-message__composer-tools"><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="Attach a file"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.5 5.5l-4.25 4.25a1.5 1.5 0 002.12 2.12L13 7.25a2.83 2.83 0 00-4-4L4.25 8a4.24 4.24 0 006 6l3.25-3.25"/></svg></button><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="Add emoji"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6"/><path d="M5.5 9.5s.9 1.5 2.5 1.5 2.5-1.5 2.5-1.5M6 6.25v.5M10 6.25v.5"/></svg></button><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="Record a voice note"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="6" y="2.5" width="4" height="7" rx="2"/><path d="M4 8a4 4 0 008 0M8 12v1.5"/></svg></button></div><button type="button" class="cn-button" data-variant="primary" data-size="sm"><span class="cn-button__label">Send</span><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 8l11-5.5-3 11-2.5-4.5L2.5 8z"/><path d="M8 9L13.5 2.5"/></svg></button></div></div></div>
                                Single bubbleOne message embedded in another surface (a notification, a quoted reply).
                                DC
                                Daniel Costa
                                Reminder: the brand review moved to Thursday at 10:00.
                                HTML
                                <div class="cn-message" data-variant="bubble" data-density="default" role="log" aria-label="Conversation with Maya Chen" style="width:100%;max-width:480px"><div class="cn-message__item" data-direction="in"><span class="cn-message__avatar" role="img" aria-label="Daniel Costa">DC</span><div class="cn-message__content"><div class="cn-message__meta"><span class="cn-message__name">Daniel Costa</span><time class="cn-message__time" datetime="2026-09-10T17:40">Yesterday · 17:40</time></div><div class="cn-message__bubble">Reminder: the brand review moved to Thursday at 10:00.</div></div></div></div>
                                Image attachment, compact densitycompact = 24px avatars, 14px text, 8px between messages, for side panels and widgets.
                                You
                                The lobby signage went up this morning.
                                SA
                                Sofia Almeida
                                Looks great in daylight. Can you send the night shot too?
                                You
                                On it.
                                HTML
                                <div class="cn-message" data-variant="thread" data-density="compact" role="log" aria-label="Conversation with Maya Chen" style="width:100%;max-width:420px"><div class="cn-message__item" data-direction="out"><div class="cn-message__content"><div class="cn-message__meta"><span class="cn-message__name">You</span><time class="cn-message__time" datetime="2026-09-11T12:02">12:02</time></div><div class="cn-message__bubble">The lobby signage went up this morning.<a href="#" class="cn-message__attachment" data-kind="image" aria-label="Open photo"><svg viewBox="0 0 320 240" role="img" aria-label="Photo of the new office lobby"><rect width="320" height="240" style="fill:var(--cn-color-bg-muted)"/><circle cx="236" cy="70" r="28" style="fill:var(--cn-color-bg-surface)"/><path d="M0 200L84 116l64 56 52-70 120 98v40H0z" style="fill:var(--cn-color-border-strong)"/></svg></a></div></div></div><div class="cn-message__item" data-direction="in"><span class="cn-message__avatar" role="img" aria-label="Sofia Almeida">SA</span><div class="cn-message__content"><div class="cn-message__meta"><span class="cn-message__name">Sofia Almeida</span><time class="cn-message__time" datetime="2026-09-11T12:05">12:05</time></div><div class="cn-message__bubble">Looks great in daylight. Can you send the night shot too?</div></div></div><div class="cn-message__item" data-direction="out"><div class="cn-message__content"><div class="cn-message__meta"><span class="cn-message__name">You</span><time class="cn-message__time" datetime="2026-09-11T12:06">12:06</time></div><div class="cn-message__bubble">On it.</div></div></div></div>
                                Disabled composerA closed conversation keeps the composer visible but disabled, with the reason as placeholder.
                                HTML
                                <div class="cn-message" data-variant="composer" data-density="default" role="log" aria-label="Conversation with Maya Chen" style="width:100%;max-width:560px"><div class="cn-message__composer" data-disabled><textarea class="cn-message__composer-input" rows="2" disabled placeholder="This conversation is closed" aria-label="Message"></textarea><div class="cn-message__composer-toolbar"><div class="cn-message__composer-tools"><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="Attach a file"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.5 5.5l-4.25 4.25a1.5 1.5 0 002.12 2.12L13 7.25a2.83 2.83 0 00-4-4L4.25 8a4.24 4.24 0 006 6l3.25-3.25"/></svg></button><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="Add emoji"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6"/><path d="M5.5 9.5s.9 1.5 2.5 1.5 2.5-1.5 2.5-1.5M6 6.25v.5M10 6.25v.5"/></svg></button><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="Record a voice note"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="6" y="2.5" width="4" height="7" rx="2"/><path d="M4 8a4 4 0 008 0M8 12v1.5"/></svg></button></div><button type="button" class="cn-button" data-variant="primary" data-size="sm"><span class="cn-button__label">Send</span><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 8l11-5.5-3 11-2.5-4.5L2.5 8z"/><path d="M8 9L13.5 2.5"/></svg></button></div></div></div>

                                Matrix

                                variant ↓ / density →defaultcompact
                                thread
                                MC
                                Maya Chen
                                Morning! Did the Q3 launch plan land in your inbox?
                                You
                                It did — reading it now. Two questions about the pricing page.
                                MC
                                Maya Chen
                                Here is the latest deck, the pricing slides start on page 12.q3-launch-plan-v4.pdfPDF · 2.4 MB
                                You
                                Perfect, thanks. I will leave comments before lunch.
                                MC
                                MC
                                Maya Chen
                                Morning! Did the Q3 launch plan land in your inbox?
                                You
                                It did — reading it now. Two questions about the pricing page.
                                MC
                                Maya Chen
                                Here is the latest deck, the pricing slides start on page 12.q3-launch-plan-v4.pdfPDF · 2.4 MB
                                You
                                Perfect, thanks. I will leave comments before lunch.
                                MC
                                composer
                                MC
                                Maya Chen
                                Morning! Did the Q3 launch plan land in your inbox?
                                You
                                It did — reading it now. Two questions about the pricing page.
                                MC
                                Maya Chen
                                Here is the latest deck, the pricing slides start on page 12.q3-launch-plan-v4.pdfPDF · 2.4 MB
                                You
                                Perfect, thanks. I will leave comments before lunch.
                                MC
                                MC
                                Maya Chen
                                Morning! Did the Q3 launch plan land in your inbox?
                                You
                                It did — reading it now. Two questions about the pricing page.
                                MC
                                Maya Chen
                                Here is the latest deck, the pricing slides start on page 12.q3-launch-plan-v4.pdfPDF · 2.4 MB
                                You
                                Perfect, thanks. I will leave comments before lunch.
                                MC
                                bubble
                                MC
                                Maya Chen
                                Morning! Did the Q3 launch plan land in your inbox?
                                You
                                It did — reading it now. Two questions about the pricing page.
                                MC
                                Maya Chen
                                Here is the latest deck, the pricing slides start on page 12.q3-launch-plan-v4.pdfPDF · 2.4 MB
                                You
                                Perfect, thanks. I will leave comments before lunch.
                                MC
                                MC
                                Maya Chen
                                Morning! Did the Q3 launch plan land in your inbox?
                                You
                                It did — reading it now. Two questions about the pricing page.
                                MC
                                Maya Chen
                                Here is the latest deck, the pricing slides start on page 12.q3-launch-plan-v4.pdfPDF · 2.4 MB
                                You
                                Perfect, thanks. I will leave comments before lunch.
                                MC
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-message <div> — The thread (role="log"): a flex column of items and date dividers with a 16px gap. Width comes from the layout.
                                • .cn-message__date-divider <div> · optional — Centered chip that separates days ("Today", "Sep 9"). role="separator".
                                • .cn-message__item <div> — One message: avatar + content in a row. data-direction="in" (received, left) or "out" (sent, right, mirrored).
                                • .cn-message__avatar <span> · optional — 32px circle with initials (24px in compact), aligned to the bottom of the bubble. Only on incoming messages; role="img" + aria-label.
                                • .cn-message__content <div> — Column with the meta line and the bubble, aligned to the start (in) or the end (out).
                                • .cn-message__meta <div> — Name and time on one line above the bubble.
                                • .cn-message__name <span> — Sender name, label-sm ("Maya Chen", "You").
                                • .cn-message__time <time> — Sent time, body-xs fg-subtle, a <time datetime>.
                                • .cn-message__bubble <div> — The message body, max 70% of the thread width, 10/14px padding, 16/24 text, radius 8 with the corner nearest the sender squared. Holds text and optional attachments.
                                • .cn-message__attachment <a> · optional — A file card (icon + name + meta) or, with data-kind="image", a picture, inside the bubble. Links to the file.
                                • .cn-message__attachment-icon <span> · optional — 32px square with a 16px file icon.
                                • .cn-message__attachment-name <span> · optional — File name, label-sm, truncated.
                                • .cn-message__attachment-meta <span> · optional — Size or type under the name, body-xs, muted.
                                • .cn-message__typing <div> · optional — Three animated dots in an incoming-style bubble. role="status" with "{Name} is typing".
                                • .cn-message__dot <span> · optional — One 6px dot of the typing indicator; the three dots bounce in sequence.
                                • .cn-message__composer <div> · optional — The reply box: hairline border, radius card, focus ring on :focus-within. Holds the textarea and the toolbar.
                                • .cn-message__composer-input <textarea> · optional — Bare <textarea>, two rows, no border; grows with content in the app.
                                • .cn-message__composer-toolbar <div> · optional — Bottom row of the composer: tool icon buttons on the left, the primary sm Send on the right.
                                • .cn-message__composer-tools <div> · optional — The group of ghost IconButtons (attach, emoji, voice) that fills the toolbar's left side.

                                Props

                                propvalues (default outlined)meaning
                                variantthread composer bubblethread = the full conversation column with dividers, messages, typing and optionally a composer at the end. composer = the root holds only the reply box (a chat that lives elsewhere, a "quick reply" footer). bubble = the root holds a single message, for embedding one quoted message in another surface.
                                densitydefault compactdefault = 32px avatars, 16/24 text, 16px between messages (full-page chat). compact = 24px avatars, 14px text, 8px between messages, for side panels and support widgets.

                                States

                                stateselectormeaning
                                incoming .cn-message__item[data-direction="in"]A received message (on the item): avatar on the left, bg-subtle bubble with the top-left corner squared. Styled in extraCss. (data-direction="in" on .cn-message__item)
                                outgoing .cn-message__item[data-direction="out"]A sent message (on the item): mirrored to the right, action-colored bubble with the top-right corner squared, no avatar. Styled in extraCss. (data-direction="out" on .cn-message__item)
                                composerFocus .cn-message__composer:focus-withinTyping in the composer: the border turns action-colored and the 3px ring appears (extraCss). (native :focus-within on .cn-message__composer)
                                composerDisabled .cn-message__composer[data-disabled]The conversation is closed or read-only: grey fill, muted text, textarea disabled (extraCss). (data-disabled on .cn-message__composer plus disabled on the textarea)

                                Rules

                                • Incoming on the left with an avatar, outgoing on the right without one. Never color-code people; direction is the only distinction.
                                • Bubbles are at most 70% of the thread width and wrap text; never a fixed width, never a scrollbar inside a bubble.
                                • Every message shows the sender and a time. Group consecutive messages from the same sender within 5 minutes under one meta line.
                                • Date dividers between days only ("Today", "Yesterday", "Sep 9"); never a divider between every message.
                                • Attachments live inside the bubble: a file card for documents, a picture (max 320px wide, radius md) for images. One attachment per bubble; more than one becomes a list.
                                • The typing indicator shows for one person at a time and disappears after 5 seconds of silence; it is never shown for the current user.
                                • The composer is the only bordered surface in the thread; tools are ghost IconButtons and Send is a primary sm Button. Enter sends, Shift+Enter breaks the line.
                                • Compact density only in side panels and widgets narrower than 480px.
                                • Long threads load older messages above with a "Load earlier messages" link Button; keep the newest message and the composer in view.

                                Accessibility

                                • Root is role="log" with an aria-label naming the conversation; new messages are appended so screen readers announce them politely.
                                • Each message exposes sender and time as text in DOM order before the body; the direction mirroring is CSS-only.
                                • Avatars with initials use role="img" + aria-label; sent messages have no avatar and say "You" in the name.
                                • The typing indicator is role="status" with an aria-label such as "Maya Chen is typing"; the dots are decorative.
                                • The composer textarea has an accessible name, tool buttons have aria-labels, and the Send button is a real <button>; Escape clears a draft only after a confirm.
                                Data display

                                MetricGroup .cn-metric-groupedit

                                The metric-card family as one component: a responsive grid of surface cards with a label, a heading-lg tabular value, a soft success/danger trend pill with a 12px arrow and a "vs last month" note. Cards can add a featured icon, a 64px sparkline, a ⋯ menu or a "View report" link under a divider, or a progress bar; the list variant stacks the same items as rows in one card.

                                Use: Use at the top of a dashboard or report page to summarize 2–4 numbers with their change. For a single KPI use Stat; for a chart with axes use ChartFrame; for counts in navigation use Counter.

                                Simple, three columnsComponent previewLabel and value only. The unit is part of the value string.
                                Monthly recurring revenue
                                $48,210
                                Active customers
                                2,318
                                Churn rate
                                1.9%
                                HTML
                                <div class="cn-metric-group" data-variant="simple" data-columns="3" role="group" aria-label="Key metrics"><div class="cn-metric-group__item"><div class="cn-metric-group__header"><span class="cn-metric-group__label">Monthly recurring revenue</span></div><div class="cn-metric-group__body"><span class="cn-metric-group__value">$48,210</span></div></div><div class="cn-metric-group__item"><div class="cn-metric-group__header"><span class="cn-metric-group__label">Active customers</span></div><div class="cn-metric-group__body"><span class="cn-metric-group__value">2,318</span></div></div><div class="cn-metric-group__item"><div class="cn-metric-group__header"><span class="cn-metric-group__label">Churn rate</span></div><div class="cn-metric-group__body"><span class="cn-metric-group__value">1.9%</span></div></div></div>
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-metric-group <div> — The grid (role="group", aria-label): 24px gaps, 2–4 equal columns that collapse to two below the lg breakpoint and one below md.
                                • .cn-metric-group__item <div> — One metric card: bg-surface, hairline, radius card, 20px padding, shadow-xs; a flex column with 16px gaps.
                                • .cn-metric-group__header <div> — Top row: optional featured icon, the label, and the optional ⋯ actions pushed right.
                                • .cn-metric-group__icon <span> · optional — Wrapper for a 40px FeaturedIcon (light theme) before the label.
                                • .cn-metric-group__label <span> — What is measured, label-sm in fg-default ("Monthly recurring revenue").
                                • .cn-metric-group__actions <div> · optional — Right-aligned actions: a ghost sm IconButton (⋯) in the header, or a link-color sm "View report" Button under the divider.
                                • .cn-metric-group__body <div> — The value on the left and, when present, the sparkline on the right, bottom-aligned.
                                • .cn-metric-group__value <span> — The number in heading-lg semibold with tabular figures; the unit is part of the string ("$48,210", "1.9%").
                                • .cn-metric-group__delta <div> · optional — Row under the value: the trend pill and its label.
                                • .cn-metric-group__trend <span> · optional — Soft pill: success for up, danger for down, neutral for flat (data-trend); add data-sentiment="good|bad" when the direction and the meaning disagree.
                                • .cn-metric-group__trend-icon <svg> · optional — 12px arrow inside the pill matching data-trend.
                                • .cn-metric-group__trend-label <span> · optional — Period note in body-sm muted ("vs last month").
                                • .cn-metric-group__chart <div> · optional — 128×64 sparkline slot; the svg strokes currentColor (bg-action) with a 10% area under the line. Decorative.
                                • .cn-metric-group__progress <div> · optional — Wrapper for a Progress bar (label + value + track) for quota-style metrics.
                                • .cn-metric-group__divider <div> · optional — Hairline that bleeds to the card edges above a footer action.

                                Props

                                propvalues (default outlined)meaning
                                variantsimple with-trend with-icon with-chart with-actions with-progress listsimple = label and value only. with-trend = adds the trend pill and period note (the default dashboard card). with-icon = a 40px FeaturedIcon before the label for a small set of very different metrics. with-chart = a sparkline next to the value for metrics with a shape over time. with-actions = a ⋯ menu in the header or a "View report" link under a divider. with-progress = a Progress bar for used-of-limit metrics (seats, storage). list = the same items as rows inside one card, for side panels and narrow columns.
                                columns2 3 4Number of equal columns on wide screens: 2 for two hero numbers or cards with charts, 3 by default, 4 for a dense KPI row. Below the lg breakpoint 3 and 4 become 2; below md everything is one column. Ignored by the list variant.

                                States

                                stateselectormeaning
                                up .cn-metric-group__trend[data-trend="up"]The value increased (on the trend pill; extraCss): success tint, up arrow. (data-trend="up" on .cn-metric-group__trend)
                                down .cn-metric-group__trend[data-trend="down"]The value decreased (extraCss): danger tint, down arrow. (data-trend="down" on .cn-metric-group__trend)
                                flat .cn-metric-group__trend[data-trend="flat"]No meaningful change (extraCss): neutral tint, a dash. (data-trend="flat" on .cn-metric-group__trend)
                                sentiment .cn-metric-group__trend[data-sentiment]When direction and meaning disagree (churn going down is good): data-sentiment="good|bad" overrides the tint while the arrow keeps the direction (extraCss). (data-sentiment="good" or "bad" on .cn-metric-group__trend)

                                Rules

                                • Two to four metrics per group. A fifth number belongs in a table or a second group with its own heading.
                                • Labels name the metric in sentence case ("Monthly recurring revenue"), never the value type; values carry their unit in the string and use tabular figures.
                                • Format numbers for the locale with at most four significant characters plus unit ("$48.2k" beats "$48,210.37" in a card).
                                • data-trend is the direction of change; when up is bad or down is good (churn, cost, response time), add data-sentiment so the tint tells the truth while the arrow keeps the direction.
                                • The period note is short and always present with a trend ("vs last month"); a trend without a period is meaningless.
                                • One variant per group: every card has the same parts, aligned on the same rows. Never mix a sparkline card with a plain one.
                                • Sparklines are decorative and unlabeled; if the shape matters enough to read, use ChartFrame.
                                • Actions are one ⋯ menu in the header or one "View report" link under the divider, never both.
                                • Use list in columns narrower than 420px or inside drawers; on a page use the grid.

                                Accessibility

                                • The root is role="group" with an aria-label ("Key metrics"); each card reads in order: label, value, trend, note.
                                • Trend arrows are aria-hidden; the trend text includes the sign or the word ("+12%", "0.4 pts") and the label gives the period, so color is never the only signal.
                                • Sparklines are aria-hidden; when a chart carries information, give it a text alternative or use ChartFrame.
                                • The ⋯ button has aria-label "More actions" and aria-haspopup="menu"; "View report" is a real link or button with the metric name available via aria-label when several cards share the text.
                                • Progress bars use role="progressbar" with aria-valuenow/min/max and a visible value.
                                Data display

                                Rating .cn-ratingedit

                                Read-only star rating: five stars (filled amber, half, or empty in the hairline color), an optional numeric value and a muted count. The badge variant packs five small stars and the value into a hairline pill; inline shows one star with the number for dense rows.

                                Use: Display reviews, satisfaction scores and quality grades next to a product, a vendor or a testimonial. It is not an input: for collecting a rating use a Radio group of stars. Do not use stars for anything that is not a rating (priority, importance).

                                Stars with value and countFour filled stars, one half star, the number and the count.
                                HTML
                                <div class="cn-rating" data-variant="stars" data-size="md" role="img" aria-label="4.5 out of 5 stars, 128 reviews"><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="half" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><span class="cn-rating__value">4.5</span><span class="cn-rating__count">(128 reviews)</span></div>
                                Stars onlyWhen the number is shown elsewhere (a heading, a table column).
                                HTML
                                <div class="cn-rating" data-variant="stars" data-size="md" role="img" aria-label="5 out of 5 stars"><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg></div>
                                Badge (proof line)Five 12px stars and the value inside a hairline pill, the count as muted text after it.
                                HTML
                                <div class="cn-rating" data-variant="badge" data-size="md" role="img" aria-label="4.9 out of 5 stars from 1,240 reviews"><span class="cn-rating__badge"><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><span class="cn-rating__value">4.9</span></span><span class="cn-rating__count">from 1,240 reviews</span></div>
                                Inline (rows and cells)One star and the number; the count in parentheses.
                                HTML
                                <div style="display:flex;flex-direction:column;gap:var(--cn-space-2);align-items:flex-start"><div class="cn-rating" data-variant="inline" data-size="md" role="img" aria-label="4.8 out of 5 stars, 312 reviews"><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><span class="cn-rating__value">4.8</span><span class="cn-rating__count">(312)</span></div><div class="cn-rating" data-variant="inline" data-size="sm" role="img" aria-label="3.6 out of 5 stars, 41 reviews"><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><span class="cn-rating__value">3.6</span><span class="cn-rating__count">(41)</span></div></div>
                                Sizes16px stars (sm) and 20px stars (md).
                                HTML
                                <div style="display:flex;flex-direction:column;gap:var(--cn-space-2);align-items:flex-start"><div class="cn-rating" data-variant="stars" data-size="sm" role="img" aria-label="4 out of 5 stars"><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="false" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><span class="cn-rating__value">4.0</span></div><div class="cn-rating" data-variant="stars" data-size="md" role="img" aria-label="4 out of 5 stars"><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="false" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><span class="cn-rating__value">4.0</span></div></div>
                                Low and emptyEmpty stars stay visible in the hairline color so the scale is always readable.
                                HTML
                                <div style="display:flex;flex-direction:column;gap:var(--cn-space-2);align-items:flex-start"><div class="cn-rating" data-variant="stars" data-size="md" role="img" aria-label="2.3 out of 5 stars, 9 reviews"><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="half" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="false" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="false" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><span class="cn-rating__value">2.3</span><span class="cn-rating__count">(9 reviews)</span></div><div class="cn-rating" data-variant="stars" data-size="md" role="img" aria-label="No ratings yet"><svg class="cn-rating__star" data-filled="false" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="false" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="false" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="false" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="false" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><span class="cn-rating__count">No ratings yet</span></div></div>

                                Recipes

                                Testimonial headerBadge above the quote, attribution in muted text below.

                                “Lumen found the buyers we had been chasing for two years in one afternoon.”

                                Elena Rossi, Head of Procurement

                                HTML
                                <div style="display:flex;flex-direction:column;gap:var(--cn-space-2);max-width:420px"><div class="cn-rating" data-variant="badge" data-size="md" role="img" aria-label="5 out of 5 stars"><span class="cn-rating__badge"><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><svg class="cn-rating__star" data-filled="true" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.5l2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L10 14.9l-5.3 2.8 1.1-5.9L1.5 7.7l5.9-.8z"/></svg><span class="cn-rating__value">5.0</span></span></div><p class="cn-text-body-md" style="margin:0">“Lumen found the buyers we had been chasing for two years in one afternoon.”</p><p class="cn-text-body-sm" style="margin:0;color:var(--cn-color-fg-muted)">Elena Rossi, Head of Procurement</p></div>

                                Matrix

                                variant ↓ / size →smmd
                                stars
                                badge
                                inline
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-rating <div> — Inline row (flex, align center, gap space.1). role="img" with aria-label "4.5 out of 5 stars, 128 reviews". Carries data-variant and data-size.
                                • .cn-rating__star <svg> — One star. data-filled="true" paints it amber, "false" the hairline color, "half" splits it with a clip on the right half. 16px (sm) or 20px (md); 12px inside the badge.
                                • .cn-rating__value <span> · optional — The number ("4.5"), label-sm, tabular. Follows the stars with a small gap.
                                • .cn-rating__count <span> · optional — Muted body-sm context: "(128 reviews)", "from 1,240 reviews".
                                • .cn-rating__badge <span> · optional — Hairline pill (surface fill, radius full, padding-inline space.2) holding five 12px stars and the value. Used by the badge variant.

                                Props

                                propvalues (default outlined)meaning
                                variantstars badge inlinestars = five stars with an optional value and count, for product pages and vendor cards; badge = a pill with five small stars and the value, followed by muted text, for testimonials and marketing proof lines; inline = a single star plus the value (and count), for table cells, list rows and compact cards.
                                sizesm mdmd = 20px stars with label-sm value (cards, headers); sm = 16px stars with label-xs value (rows, dense lists). The badge always uses 12px stars.

                                Rules

                                • Always five stars. Fill from the left; a fraction from .25 to .75 is a half star, never a partial fill at other widths.
                                • Filled stars are amber, empty stars the hairline color. Never recolor stars by brand or by score; the number carries the nuance.
                                • Show the value with one decimal ("4.5", "4.0") and the count with a thousands separator ("1,240 reviews"). Round the value, never the count.
                                • stars on product and vendor pages; inline in tables, lists and compact cards; badge only on marketing and testimonial blocks.
                                • With fewer than 5 reviews show the count and no value; with 0 show empty stars and "No ratings yet".
                                • Rating is display only. Collecting a rating is a form control (Radio group of stars) with its own focus and keyboard behavior.
                                • Sizes match the surrounding text: sm next to body-sm, md next to body-md and headings.

                                Accessibility

                                • The root is role="img" with aria-label that states the score and the count ("4.5 out of 5 stars, 128 reviews"); the stars are aria-hidden.
                                • The visible value and count repeat the aria-label content, so sighted and screen-reader users get the same information.
                                • Color is not the only cue: filled and empty stars differ in luminance and the number is always available nearby or in the label.
                                Data display

                                TreeView .cn-tree-viewedit

                                A hierarchy of rows that expand and collapse: a chevron that rotates when open, a 20px folder or file icon, the label and an optional count, with nested groups indented 24px and an optional guide line. Rows are 36px (32px small), hover in bg-subtle, the selected row keeps the fill with a semibold label.

                                Use: Use for file systems, folder structures, nested categories and org charts where depth matters and people open branches on demand. For a flat list use List; for navigation with two levels use SidebarNav; for a table with expandable rows use Table.

                                Three levels, one selectedOpen folders rotate the chevron; the leaf rows hide it. "landing-page.fig" is selected; "Finance" is collapsed with a count.
                                • Lumen workspace
                                  • Brand
                                    • lumen-logo.svg
                                    • brand-guidelines.pdf
                                  • Marketing
                                    • Q3 campaign2
                                      • landing-page.fig
                                      • copy-deck.docx
                                HTML
                                <ul class="cn-tree-view" data-size="md" data-variant="default" role="tree" aria-label="Lumen workspace files" style="width:100%;max-width:320px"><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="1" aria-expanded="true" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.75 5.5A1.5 1.5 0 014.25 4h3.5l2 2h6a1.5 1.5 0 011.5 1.5v7A1.5 1.5 0 0115.75 16h-11.5a1.5 1.5 0 01-1.5-1.5v-9z"/></svg></span><span class="cn-tree-view__label">Lumen workspace</span></div><ul class="cn-tree-view__group" role="group"><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="2" aria-expanded="true" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.75 5.5A1.5 1.5 0 014.25 4h3.5l2 2h6a1.5 1.5 0 011.5 1.5v7A1.5 1.5 0 0115.75 16h-11.5a1.5 1.5 0 01-1.5-1.5v-9z"/></svg></span><span class="cn-tree-view__label">Brand</span></div><ul class="cn-tree-view__group" role="group"><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="3" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5.25 3h6.5l3.5 3.5V16a1 1 0 01-1 1h-9a1 1 0 01-1-1V4a1 1 0 011-1z"/><path d="M11.5 3v4h4"/></svg></span><span class="cn-tree-view__label">lumen-logo.svg</span></div></li><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="3" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5.25 3h6.5l3.5 3.5V16a1 1 0 01-1 1h-9a1 1 0 01-1-1V4a1 1 0 011-1z"/><path d="M11.5 3v4h4"/></svg></span><span class="cn-tree-view__label">brand-guidelines.pdf</span></div></li></ul></li><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="2" aria-expanded="true" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.75 5.5A1.5 1.5 0 014.25 4h3.5l2 2h6a1.5 1.5 0 011.5 1.5v7A1.5 1.5 0 0115.75 16h-11.5a1.5 1.5 0 01-1.5-1.5v-9z"/></svg></span><span class="cn-tree-view__label">Marketing</span></div><ul class="cn-tree-view__group" role="group"><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="3" aria-expanded="true" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.75 5.5A1.5 1.5 0 014.25 4h3.5l2 2h6a1.5 1.5 0 011.5 1.5v7A1.5 1.5 0 0115.75 16h-11.5a1.5 1.5 0 01-1.5-1.5v-9z"/></svg></span><span class="cn-tree-view__label">Q3 campaign</span><span class="cn-tree-view__badge">2</span></div><ul class="cn-tree-view__group" role="group"><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="4" aria-selected="true" tabindex="0"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5.25 3h6.5l3.5 3.5V16a1 1 0 01-1 1h-9a1 1 0 01-1-1V4a1 1 0 011-1z"/><path d="M11.5 3v4h4"/></svg></span><span class="cn-tree-view__label">landing-page.fig</span></div></li><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="4" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5.25 3h6.5l3.5 3.5V16a1 1 0 01-1 1h-9a1 1 0 01-1-1V4a1 1 0 011-1z"/><path d="M11.5 3v4h4"/></svg></span><span class="cn-tree-view__label">copy-deck.docx</span></div></li></ul></li></ul></li><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="2" aria-expanded="false" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.75 5.5A1.5 1.5 0 014.25 4h3.5l2 2h6a1.5 1.5 0 011.5 1.5v7A1.5 1.5 0 0115.75 16h-11.5a1.5 1.5 0 01-1.5-1.5v-9z"/></svg></span><span class="cn-tree-view__label">Finance</span><span class="cn-tree-view__badge">24</span></div></li></ul></li></ul>
                                With guide lines, small32px rows and a 1px guide under every open parent.
                                • Lumen workspace
                                  • Brand
                                    • lumen-logo.svg
                                    • brand-guidelines.pdf
                                  • Marketing
                                    • Q3 campaign2
                                      • landing-page.fig
                                      • copy-deck.docx
                                HTML
                                <ul class="cn-tree-view" data-size="sm" data-variant="with-lines" role="tree" aria-label="Lumen workspace files" style="width:100%;max-width:280px"><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="1" aria-expanded="true" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.75 5.5A1.5 1.5 0 014.25 4h3.5l2 2h6a1.5 1.5 0 011.5 1.5v7A1.5 1.5 0 0115.75 16h-11.5a1.5 1.5 0 01-1.5-1.5v-9z"/></svg></span><span class="cn-tree-view__label">Lumen workspace</span></div><ul class="cn-tree-view__group" role="group"><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="2" aria-expanded="true" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.75 5.5A1.5 1.5 0 014.25 4h3.5l2 2h6a1.5 1.5 0 011.5 1.5v7A1.5 1.5 0 0115.75 16h-11.5a1.5 1.5 0 01-1.5-1.5v-9z"/></svg></span><span class="cn-tree-view__label">Brand</span></div><ul class="cn-tree-view__group" role="group"><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="3" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5.25 3h6.5l3.5 3.5V16a1 1 0 01-1 1h-9a1 1 0 01-1-1V4a1 1 0 011-1z"/><path d="M11.5 3v4h4"/></svg></span><span class="cn-tree-view__label">lumen-logo.svg</span></div></li><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="3" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5.25 3h6.5l3.5 3.5V16a1 1 0 01-1 1h-9a1 1 0 01-1-1V4a1 1 0 011-1z"/><path d="M11.5 3v4h4"/></svg></span><span class="cn-tree-view__label">brand-guidelines.pdf</span></div></li></ul></li><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="2" aria-expanded="true" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.75 5.5A1.5 1.5 0 014.25 4h3.5l2 2h6a1.5 1.5 0 011.5 1.5v7A1.5 1.5 0 0115.75 16h-11.5a1.5 1.5 0 01-1.5-1.5v-9z"/></svg></span><span class="cn-tree-view__label">Marketing</span></div><ul class="cn-tree-view__group" role="group"><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="3" aria-expanded="true" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.75 5.5A1.5 1.5 0 014.25 4h3.5l2 2h6a1.5 1.5 0 011.5 1.5v7A1.5 1.5 0 0115.75 16h-11.5a1.5 1.5 0 01-1.5-1.5v-9z"/></svg></span><span class="cn-tree-view__label">Q3 campaign</span><span class="cn-tree-view__badge">2</span></div><ul class="cn-tree-view__group" role="group"><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="4" aria-selected="true" tabindex="0"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5.25 3h6.5l3.5 3.5V16a1 1 0 01-1 1h-9a1 1 0 01-1-1V4a1 1 0 011-1z"/><path d="M11.5 3v4h4"/></svg></span><span class="cn-tree-view__label">landing-page.fig</span></div></li><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="4" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5.25 3h6.5l3.5 3.5V16a1 1 0 01-1 1h-9a1 1 0 01-1-1V4a1 1 0 011-1z"/><path d="M11.5 3v4h4"/></svg></span><span class="cn-tree-view__label">copy-deck.docx</span></div></li></ul></li></ul></li><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="2" aria-expanded="false" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.75 5.5A1.5 1.5 0 014.25 4h3.5l2 2h6a1.5 1.5 0 011.5 1.5v7A1.5 1.5 0 0115.75 16h-11.5a1.5 1.5 0 01-1.5-1.5v-9z"/></svg></span><span class="cn-tree-view__label">Finance</span><span class="cn-tree-view__badge">24</span></div></li></ul></li></ul>
                                With checkboxes (multi-select)The parent of a partially checked branch is indeterminate; checking a parent checks all of its children.
                                • Lumen workspace
                                  • Brand
                                    • lumen-logo.svg
                                    • brand-guidelines.pdf
                                  • Marketing
                                    • Q3 campaign2
                                      • landing-page.fig
                                      • copy-deck.docx
                                HTML
                                <ul class="cn-tree-view" data-size="md" data-variant="with-checkbox" role="tree" aria-label="Lumen workspace files" style="width:100%;max-width:360px"><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="1" aria-expanded="true" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><label class="cn-checkbox" data-size="sm" data-indeterminate><input class="cn-checkbox__input" type="checkbox" aria-label="Select Lumen workspace"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.75 5.5A1.5 1.5 0 014.25 4h3.5l2 2h6a1.5 1.5 0 011.5 1.5v7A1.5 1.5 0 0115.75 16h-11.5a1.5 1.5 0 01-1.5-1.5v-9z"/></svg></span><span class="cn-tree-view__label">Lumen workspace</span></div><ul class="cn-tree-view__group" role="group"><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="2" aria-expanded="true" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select Brand" checked><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.75 5.5A1.5 1.5 0 014.25 4h3.5l2 2h6a1.5 1.5 0 011.5 1.5v7A1.5 1.5 0 0115.75 16h-11.5a1.5 1.5 0 01-1.5-1.5v-9z"/></svg></span><span class="cn-tree-view__label">Brand</span></div><ul class="cn-tree-view__group" role="group"><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="3" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select lumen-logo.svg" checked><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5.25 3h6.5l3.5 3.5V16a1 1 0 01-1 1h-9a1 1 0 01-1-1V4a1 1 0 011-1z"/><path d="M11.5 3v4h4"/></svg></span><span class="cn-tree-view__label">lumen-logo.svg</span></div></li><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="3" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select brand-guidelines.pdf" checked><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5.25 3h6.5l3.5 3.5V16a1 1 0 01-1 1h-9a1 1 0 01-1-1V4a1 1 0 011-1z"/><path d="M11.5 3v4h4"/></svg></span><span class="cn-tree-view__label">brand-guidelines.pdf</span></div></li></ul></li><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="2" aria-expanded="true" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select Marketing"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.75 5.5A1.5 1.5 0 014.25 4h3.5l2 2h6a1.5 1.5 0 011.5 1.5v7A1.5 1.5 0 0115.75 16h-11.5a1.5 1.5 0 01-1.5-1.5v-9z"/></svg></span><span class="cn-tree-view__label">Marketing</span></div><ul class="cn-tree-view__group" role="group"><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="3" aria-expanded="true" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select Q3 campaign"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.75 5.5A1.5 1.5 0 014.25 4h3.5l2 2h6a1.5 1.5 0 011.5 1.5v7A1.5 1.5 0 0115.75 16h-11.5a1.5 1.5 0 01-1.5-1.5v-9z"/></svg></span><span class="cn-tree-view__label">Q3 campaign</span><span class="cn-tree-view__badge">2</span></div><ul class="cn-tree-view__group" role="group"><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="4" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select landing-page.fig"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5.25 3h6.5l3.5 3.5V16a1 1 0 01-1 1h-9a1 1 0 01-1-1V4a1 1 0 011-1z"/><path d="M11.5 3v4h4"/></svg></span><span class="cn-tree-view__label">landing-page.fig</span></div></li><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="4" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select copy-deck.docx"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5.25 3h6.5l3.5 3.5V16a1 1 0 01-1 1h-9a1 1 0 01-1-1V4a1 1 0 011-1z"/><path d="M11.5 3v4h4"/></svg></span><span class="cn-tree-view__label">copy-deck.docx</span></div></li></ul></li></ul></li><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="2" aria-expanded="false" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select Finance"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.75 5.5A1.5 1.5 0 014.25 4h3.5l2 2h6a1.5 1.5 0 011.5 1.5v7A1.5 1.5 0 0115.75 16h-11.5a1.5 1.5 0 01-1.5-1.5v-9z"/></svg></span><span class="cn-tree-view__label">Finance</span><span class="cn-tree-view__badge">24</span></div></li></ul></li></ul>
                                Disabled rowaria-disabled rows stay visible with a disabled color and no hover.
                                • Shared with me
                                  • Board deck (view only)
                                  • Legal3
                                HTML
                                <ul class="cn-tree-view" data-size="md" data-variant="default" role="tree" aria-label="Lumen workspace files" style="width:100%;max-width:280px"><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="1" aria-expanded="true" aria-selected="true" tabindex="0"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.75 5.5A1.5 1.5 0 014.25 4h3.5l2 2h6a1.5 1.5 0 011.5 1.5v7A1.5 1.5 0 0115.75 16h-11.5a1.5 1.5 0 01-1.5-1.5v-9z"/></svg></span><span class="cn-tree-view__label">Shared with me</span></div><ul class="cn-tree-view__group" role="group"><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="2" aria-selected="false" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5.25 3h6.5l3.5 3.5V16a1 1 0 01-1 1h-9a1 1 0 01-1-1V4a1 1 0 011-1z"/><path d="M11.5 3v4h4"/></svg></span><span class="cn-tree-view__label">Board deck (view only)</span></div></li><li role="none"><div class="cn-tree-view__item" role="treeitem" aria-level="2" aria-selected="false" aria-disabled="true" tabindex="-1"><span class="cn-tree-view__toggle" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span><span class="cn-tree-view__icon"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.75 5.5A1.5 1.5 0 014.25 4h3.5l2 2h6a1.5 1.5 0 011.5 1.5v7A1.5 1.5 0 0115.75 16h-11.5a1.5 1.5 0 01-1.5-1.5v-9z"/></svg></span><span class="cn-tree-view__label">Legal</span><span class="cn-tree-view__badge">3</span></div></li></ul></li></ul>

                                Matrix

                                variant ↓ / size →smmd
                                default
                                • Lumen workspace
                                  • Brand
                                    • lumen-logo.svg
                                    • brand-guidelines.pdf
                                  • Marketing
                                    • Q3 campaign2
                                      • landing-page.fig
                                      • copy-deck.docx
                                • Lumen workspace
                                  • Brand
                                    • lumen-logo.svg
                                    • brand-guidelines.pdf
                                  • Marketing
                                    • Q3 campaign2
                                      • landing-page.fig
                                      • copy-deck.docx
                                with-lines
                                • Lumen workspace
                                  • Brand
                                    • lumen-logo.svg
                                    • brand-guidelines.pdf
                                  • Marketing
                                    • Q3 campaign2
                                      • landing-page.fig
                                      • copy-deck.docx
                                • Lumen workspace
                                  • Brand
                                    • lumen-logo.svg
                                    • brand-guidelines.pdf
                                  • Marketing
                                    • Q3 campaign2
                                      • landing-page.fig
                                      • copy-deck.docx
                                with-checkbox
                                • Lumen workspace
                                  • Brand
                                    • lumen-logo.svg
                                    • brand-guidelines.pdf
                                  • Marketing
                                    • Q3 campaign2
                                      • landing-page.fig
                                      • copy-deck.docx
                                • Lumen workspace
                                  • Brand
                                    • lumen-logo.svg
                                    • brand-guidelines.pdf
                                  • Marketing
                                    • Q3 campaign2
                                      • landing-page.fig
                                      • copy-deck.docx
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-tree-view <ul> — The tree (role="tree", aria-label). A list; nested groups are <ul role="group"> after their parent row inside a <li role="none">.
                                • .cn-tree-view__item <div> — One row (role="treeitem", aria-level, aria-expanded when it has children, aria-selected, roving tabindex). 36px tall, 8px horizontal padding, radius md, body-md.
                                • .cn-tree-view__toggle <span> — 16px chevron on the left; rotates 90° when the row is aria-expanded="true"; invisible on leaf rows so labels stay aligned. aria-hidden.
                                • .cn-tree-view__icon <span> — 20px folder or file icon in fg-muted.
                                • .cn-tree-view__label <span> — The name, truncated with an ellipsis; semibold when the row is selected.
                                • .cn-tree-view__badge <span> · optional — Optional count pill at the end of the row (children, unread), label-xs in bg-subtle.
                                • .cn-tree-view__group <ul> · optional — The nested list of children (role="group"), indented by 24px; in the with-lines variant it draws a 1px guide under the parent chevron.

                                Props

                                propvalues (default outlined)meaning
                                sizesm mdmd = 36px rows with body-md text and 20px icons (file browsers, settings trees). sm = 32px rows with body-sm text and 16px icons for side panels and pickers.
                                variantdefault with-lines with-checkboxdefault = indentation only. with-lines = a 1px guide line under each open parent so deep trees stay readable. with-checkbox = a sm Checkbox before the icon on every row for multi-select (moving files, choosing folders to sync); parents show indeterminate when only some children are checked.

                                States

                                stateselectormeaning
                                hover .cn-tree-view__item:hoverPointer over a row (on the item; extraCss): bg-subtle fill. (native :hover on .cn-tree-view__item)
                                selected .cn-tree-view__item[aria-selected="true"]The chosen row (on the item; extraCss): bg-subtle fill, ink text, semibold label, ink icon. (aria-selected="true" on .cn-tree-view__item)
                                expanded .cn-tree-view__item[aria-expanded="true"]An open parent (on the item; extraCss): the chevron rotates 90°. aria-expanded="false" hides the following group. (aria-expanded="true|false" on .cn-tree-view__item)
                                focus .cn-tree-view__item:focus-visibleKeyboard focus on a row shows the 3px ring (extraCss). (native :focus-visible on .cn-tree-view__item)
                                disabled .cn-tree-view__item[aria-disabled="true"]A row that cannot be chosen (no permission): disabled text, no hover (extraCss). (aria-disabled="true" on .cn-tree-view__item)

                                Rules

                                • Rows show one name each, truncated with an ellipsis; the full name lives in a title or Tooltip. Never wrap a row onto two lines.
                                • Folders before files, both alphabetical, unless the user chose another sort. Do not mix sort orders between levels.
                                • Only parents get a chevron; leaves keep an invisible 16px slot so all labels at one level align.
                                • Exactly one selected row in single-select trees; the selected row is the one whose content is shown next to the tree.
                                • Counts in the badge are for children or unread items and update live; never use the badge for status words.
                                • Use with-lines when trees go deeper than three levels; at two levels the indentation is enough.
                                • with-checkbox is for choosing many nodes at once (move, sync, export); the row click still selects, only the box toggles.
                                • Deep trees (more than five levels) are a design smell: flatten with breadcrumbs or a search instead.
                                • sm inside side panels and pickers; md in a file browser or settings page where the tree is the main content.

                                Accessibility

                                • Root has role="tree" and an aria-label; rows are role="treeitem" with aria-level, aria-expanded on parents and aria-selected; child lists are role="group" placed right after their parent row. Add aria-owns on the parent when your framework needs explicit ownership.
                                • Roving tabindex: one row is tabbable; Up/Down move, Right expands or moves into children, Left collapses or moves to the parent, Home/End jump, Enter activates, typing jumps to a matching name.
                                • The chevron and icons are aria-hidden; the label is the accessible name. Counts are read as text after the name.
                                • In with-checkbox each Checkbox has an aria-label "Select {name}"; Space toggles the box while the row keeps its own selection.
                                • Selection and hover are conveyed by fill and weight, never by color alone; the disabled state uses aria-disabled so the row is still announced.
                                Feedback

                                Alert .cn-alertedit

                                Callout with a featured icon (a 20px line icon inside two faint concentric rings), a semibold title, a one-line description, optional link actions and a close button. 16px padding, 12px radius; tinted (soft) or white (outline); in the flow of the page, floating with a shadow, or as a full-width bar.

                                Use: Use for messages tied to the content on screen: a failed sync, a plan limit, a confirmation after a form saves, a release note above a list. Not for transient feedback (use Toast), not for one-line page-wide notices (use Banner), and not for decisions that block the flow (use Dialog).

                                Default (outline, neutral, inline)The "default" alert: white surface, gray featured icon, two link actions (gray then brand), close button top-right.
                                We’ve just released a new feature

                                Check out the all new dashboard view. Pages now load faster.

                                HTML
                                <div class="cn-alert" data-tone="neutral" data-variant="outline" data-layout="inline" role="status" style="max-width:640px"><span class="cn-alert__icon" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg></span><div class="cn-alert__content"><div class="cn-alert__title">We’ve just released a new feature</div><p class="cn-alert__description">Check out the all new dashboard view. Pages now load faster.</p><div class="cn-alert__actions"><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">Dismiss</span></button><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">View changes</span></button></div></div><button type="button" class="cn-alert__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                Brand (soft)
                                We’ve just released a new feature

                                Check out the all new dashboard view. Pages now load faster.

                                HTML
                                <div class="cn-alert" data-tone="brand" data-variant="soft" data-layout="inline" role="status" style="max-width:640px"><span class="cn-alert__icon" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 1.5l1.6 4.9 4.9 1.6-4.9 1.6L8 14.5l-1.6-4.9L1.5 8l4.9-1.6z"/></svg></span><div class="cn-alert__content"><div class="cn-alert__title">We’ve just released a new feature</div><p class="cn-alert__description">Check out the all new dashboard view. Pages now load faster.</p><div class="cn-alert__actions"><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">Dismiss</span></button><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">View changes</span></button></div></div><button type="button" class="cn-alert__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                Info (soft)
                                A new export format is available

                                You can now download reports as an Excel workbook with one sheet per team.

                                HTML
                                <div class="cn-alert" data-tone="info" data-variant="soft" data-layout="inline" role="status" style="max-width:640px"><span class="cn-alert__icon" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg></span><div class="cn-alert__content"><div class="cn-alert__title">A new export format is available</div><p class="cn-alert__description">You can now download reports as an Excel workbook with one sheet per team.</p></div><button type="button" class="cn-alert__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                Success (soft)
                                Successfully updated profile

                                Your changes have been saved and will appear in the team directory within a few minutes.

                                HTML
                                <div class="cn-alert" data-tone="success" data-variant="soft" data-layout="inline" role="status" style="max-width:640px"><span class="cn-alert__icon" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><div class="cn-alert__content"><div class="cn-alert__title">Successfully updated profile</div><p class="cn-alert__description">Your changes have been saved and will appear in the team directory within a few minutes.</p><div class="cn-alert__actions"><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">Dismiss</span></button><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">View profile</span></button></div></div><button type="button" class="cn-alert__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                Warning (soft)
                                HTML
                                <div class="cn-alert" data-tone="warning" data-variant="soft" data-layout="inline" role="alert" style="max-width:640px"><span class="cn-alert__icon" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 2.5l6 11H2l6-11zM8 7v3M8 11.5v.5"/></svg></span><div class="cn-alert__content"><div class="cn-alert__title">Your trial ends in 3 days</div><p class="cn-alert__description">Add a payment method to keep your projects and team members after 14 Sep.</p><div class="cn-alert__actions"><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">Dismiss</span></button><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">Upgrade plan</span></button></div></div><button type="button" class="cn-alert__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                Danger (soft)Danger alerts have no close button while the problem is open; they leave when it is fixed.
                                HTML
                                <div class="cn-alert" data-tone="danger" data-variant="soft" data-layout="inline" role="alert" style="max-width:640px"><span class="cn-alert__icon" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 2.5l6 11H2l6-11zM8 7v3M8 11.5v.5"/></svg></span><div class="cn-alert__content"><div class="cn-alert__title">There was a problem with that action</div><p class="cn-alert__description">We couldn’t save your changes. Check your connection and try again.</p><div class="cn-alert__actions"><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">Dismiss</span></button><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">Try again</span></button></div></div></div>
                                Floating (shadow-lg)Same card with shadow-lg, for alerts rendered over the page (bottom of the viewport, above a table).
                                We’ve just released a new feature

                                Check out the all new dashboard view. Pages now load faster.

                                HTML
                                <div class="cn-alert" data-tone="neutral" data-variant="outline" data-layout="floating" role="status" style="max-width:640px"><span class="cn-alert__icon" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg></span><div class="cn-alert__content"><div class="cn-alert__title">We’ve just released a new feature</div><p class="cn-alert__description">Check out the all new dashboard view. Pages now load faster.</p><div class="cn-alert__actions"><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">Dismiss</span></button><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">View changes</span></button></div></div><button type="button" class="cn-alert__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                Full-width barEdge to edge under the topbar: square corners, bottom hairline only, actions on the right as a direct child of the root.
                                We’ve just released a new feature

                                Check out the all new dashboard view. Pages now load faster.

                                HTML
                                <div class="cn-alert" data-tone="neutral" data-variant="outline" data-layout="full-width" role="status"><span class="cn-alert__icon" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg></span><div class="cn-alert__content"><div class="cn-alert__title">We’ve just released a new feature</div><p class="cn-alert__description">Check out the all new dashboard view. Pages now load faster.</p></div><div class="cn-alert__actions"><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">Dismiss</span></button><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">View changes</span></button></div><button type="button" class="cn-alert__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>

                                Matrix

                                variant ↓ / tone →neutralbrandinfosuccesswarningdanger
                                soft
                                We’ve just released a new feature

                                Check out the all new dashboard view. Pages now load faster.

                                We’ve just released a new feature

                                Check out the all new dashboard view. Pages now load faster.

                                We’ve just released a new feature

                                Check out the all new dashboard view. Pages now load faster.

                                We’ve just released a new feature

                                Check out the all new dashboard view. Pages now load faster.

                                We’ve just released a new feature

                                Check out the all new dashboard view. Pages now load faster.

                                We’ve just released a new feature

                                Check out the all new dashboard view. Pages now load faster.

                                outline
                                We’ve just released a new feature

                                Check out the all new dashboard view. Pages now load faster.

                                We’ve just released a new feature

                                Check out the all new dashboard view. Pages now load faster.

                                We’ve just released a new feature

                                Check out the all new dashboard view. Pages now load faster.

                                We’ve just released a new feature

                                Check out the all new dashboard view. Pages now load faster.

                                We’ve just released a new feature

                                Check out the all new dashboard view. Pages now load faster.

                                We’ve just released a new feature

                                Check out the all new dashboard view. Pages now load faster.

                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-alert <div> — The callout surface. role="alert" for danger/warning that appear dynamically, role="status" otherwise. Carries data-tone, data-variant, data-layout.
                                • .cn-alert__icon <span> — Featured icon, "outline" style: a 20px line icon (ICON.info / ICON.check / ICON.warning inside the span) in the tone color with two concentric 2px rings at 30% and 10% opacity (28px and 38px). aria-hidden.
                                • .cn-alert__content <div> — Text column: title, description, actions. Flexes to fill; min-width 0 so long text wraps.
                                • .cn-alert__title <div> — text-sm semibold (label-md), one line. Says what happened ("We’ve just released a new feature"), not "Error".
                                • .cn-alert__description <p> · optional — text-sm muted (body-md, fg-muted), one or two sentences under the title. Says what to do next.
                                • .cn-alert__actions <div> · optional — Row of link Buttons (size sm), 12px apart: a quiet one first ("Dismiss", gray) then the one that does something ("View changes", brand). At most two. Inside the content column, or a direct child of the root in the full-width layout (then it sits on the right).
                                • .cn-alert__dismiss <button> · optional — Close button in the top-right corner: 36px hit area, 20px X icon, fg-subtle, 8px from the edges. Requires aria-label="Dismiss".

                                Props

                                propvalues (default outlined)meaning
                                toneneutral brand info success warning dangerMeaning. neutral = context or a tip (gray icon; "default"/"gray"); brand = product news and upsell (brand icon, brand tint); info = something to know, no action needed (blue); success = a completed action; warning = needs attention soon, nothing is broken; danger = failed or blocked, the user must act ("error").
                                variantsoft outlinesoft = tinted background (tone-50) with the tone border; outline = white surface with the neutral hairline and only the icon in the tone color ("default" alert; quieter, for lists of alerts or inside cards).
                                layoutinline floating full-widthinline = sits in the content flow above the thing it describes, no shadow (default); floating = the same card with shadow-lg, for alerts rendered over the page (bottom of the viewport, over a table); full-width = edge-to-edge bar with square corners, a bottom hairline and the actions on the right ("full-width" alert), placed directly under the topbar or at the top of a section.

                                States

                                stateselectormeaning
                                dismissHover .cn-alert__dismiss:hoverPointer over the close button: subtle fill, icon one step darker. Styled in extraCss because it lives on a child. (native :hover on .cn-alert__dismiss)
                                dismissFocus .cn-alert__dismiss:focus-visibleKeyboard focus on the close button shows the focus ring. Styled in extraCss. (native :focus-visible on .cn-alert__dismiss)

                                Rules

                                • Title says what happened in plain words ("We’ve just released a new feature"); the description says what to do next. Never "Error" or "Warning" as the title.
                                • Keep it to one title plus at most two sentences. If it needs more, link to a page.
                                • At most two actions, size sm, variant link: the quiet one first ("Dismiss", rendered gray), the useful one last ("View changes", brand). Never a primary or danger Button inside an Alert.
                                • Text stays neutral in every tone (title fg-default, description fg-muted). Only the icon, the border and the tint carry the color, so a soft danger alert is still readable.
                                • inline alerts sit at the top of the region they describe (above the form, inside the card); floating alerts are the same card with shadow-lg over the page; full-width bars go directly under the topbar or at the top of a section, never inside a card.
                                • Use soft by default. Use outline (the "default" alert) when several alerts stack, when the alert is inside another tinted surface, or when the message is neutral news.
                                • Only neutral, brand, info and success alerts are dismissible without resolving anything. Danger alerts disappear when the problem is fixed, not when the user closes them.
                                • Icon is required and comes from the tone: neutral/info/brand → ICON.info (or ICON.spark for brand news), success → ICON.check, warning/danger → ICON.warning. Wrap it in the icon span; the rings are drawn by CSS.
                                • Do not animate alerts in or out; they appear with the content.

                                Accessibility

                                • role="alert" for warning/danger alerts inserted after load (assistive tech announces them immediately); role="status" for neutral/brand/info/success.
                                • The featured icon is decorative (aria-hidden on the span); the title carries the meaning, so color is never the only signal.
                                • The close button needs aria-label="Dismiss" and is the last focusable element inside the alert.
                                • Actions are real <button> or <a> elements in reading order after the description.
                                Feedback

                                Banner .cn-banneredit

                                Full-width strip at the top of the page or a section: one line of text, an optional icon, one action and a dismiss. Square corners, no shadow, a hairline at the bottom; it reads as part of the chrome, not as a card.

                                Use: Use for one notice that concerns the whole page or account: maintenance tonight, trial ending, a product announcement (tone accent), an outage (tone danger). Not for feedback on a specific element (use Alert) and not for transient confirmations (use Toast).

                                Neutral (soft)
                                Scheduled maintenance on Saturday 14 Sep, 02:00–03:00 UTC. Outreach pauses during that hour.
                                HTML
                                <div class="cn-banner" data-tone="neutral" data-variant="soft" role="status"><svg class="cn-banner__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg><div class="cn-banner__content"><strong>Scheduled maintenance</strong> on Saturday 14 Sep, 02:00–03:00 UTC. Outreach pauses during that hour.</div><div class="cn-banner__actions"><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">Details</span></button></div><button type="button" class="cn-banner__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                Info
                                Your trial ends in 6 days. Add a payment method to keep your prospects and drafts.
                                HTML
                                <div class="cn-banner" data-tone="info" data-variant="soft" role="status"><svg class="cn-banner__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg><div class="cn-banner__content">Your trial ends in 6 days. Add a payment method to keep your prospects and drafts.</div><div class="cn-banner__actions"><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Add payment method</span></button></div><button type="button" class="cn-banner__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                Warning
                                The Gmail connection for sales@verolabs.com expires tomorrow. Reconnect to avoid a gap in replies.
                                HTML
                                <div class="cn-banner" data-tone="warning" data-variant="soft" role="status"><svg class="cn-banner__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 2.5l6 11H2l6-11zM8 7v3M8 11.5v.5"/></svg><div class="cn-banner__content">The Gmail connection for <strong>sales@verolabs.com</strong> expires tomorrow. Reconnect to avoid a gap in replies.</div><div class="cn-banner__actions"><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Reconnect</span></button></div></div>
                                Accent (announcement)
                                New: agents now read replies in Japanese and German and draft answers in the buyer’s language.
                                HTML
                                <div class="cn-banner" data-tone="accent" data-variant="soft" role="status"><svg class="cn-banner__icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 1.5l1.6 4.9 4.9 1.6-4.9 1.6L8 14.5l-1.6-4.9L1.5 8l4.9-1.6z"/></svg><div class="cn-banner__content"><strong>New:</strong> agents now read replies in Japanese and German and draft answers in the buyer’s language.</div><div class="cn-banner__actions"><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">See what changed</span></button></div><button type="button" class="cn-banner__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                Danger (solid)solid is reserved for danger and info notices that must not be missed. No dismiss while the incident is open.
                                HTML
                                <div class="cn-banner" data-tone="danger" data-variant="solid" role="alert"><svg class="cn-banner__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 2.5l6 11H2l6-11zM8 7v3M8 11.5v.5"/></svg><div class="cn-banner__content">Email delivery is degraded since 09:12 UTC. Sent messages may arrive late; nothing is lost.</div><div class="cn-banner__actions"><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Status page</span></button></div></div>

                                Matrix

                                variant ↓ / tone →neutralinfowarningdangeraccent
                                soft
                                Scheduled maintenance on Saturday 14 Sep, 02:00–03:00 UTC. Outreach pauses during that hour.
                                Scheduled maintenance on Saturday 14 Sep, 02:00–03:00 UTC. Outreach pauses during that hour.
                                Scheduled maintenance on Saturday 14 Sep, 02:00–03:00 UTC. Outreach pauses during that hour.
                                Scheduled maintenance on Saturday 14 Sep, 02:00–03:00 UTC. Outreach pauses during that hour.
                                Scheduled maintenance on Saturday 14 Sep, 02:00–03:00 UTC. Outreach pauses during that hour.
                                solid
                                Scheduled maintenance on Saturday 14 Sep, 02:00–03:00 UTC. Outreach pauses during that hour.
                                Scheduled maintenance on Saturday 14 Sep, 02:00–03:00 UTC. Outreach pauses during that hour.
                                Scheduled maintenance on Saturday 14 Sep, 02:00–03:00 UTC. Outreach pauses during that hour.
                                Scheduled maintenance on Saturday 14 Sep, 02:00–03:00 UTC. Outreach pauses during that hour.
                                Scheduled maintenance on Saturday 14 Sep, 02:00–03:00 UTC. Outreach pauses during that hour.
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-banner <div> — The strip. Spans the full width of its container; role="status" (or "alert" for danger).
                                • .cn-banner__icon <svg> · optional — 16px icon in the current text color. Decorative.
                                • .cn-banner__content <div> — The message, one sentence, body-sm. Flexes to fill; may contain a <strong> lead.
                                • .cn-banner__actions <div> · optional — One link or outline Button (size sm) on the right.
                                • .cn-banner__dismiss <button> · optional — 24px ghost icon button with an X at the far end. aria-label="Dismiss".

                                Props

                                propvalues (default outlined)meaning
                                toneneutral info warning danger accentneutral = housekeeping notice; info = something to know; warning = act before a deadline; danger = something is broken now; accent = brand announcement (new feature, launch) in the accent wash — never for status.
                                variantsoft solidsoft = tinted background with dark tone text (default); solid = filled tone background with white text, reserved for danger (outage) and info (maintenance) when the notice must not be missed.

                                States

                                stateselectormeaning
                                dismissHover .cn-banner__dismiss:hoverPointer over the dismiss button: faint current-color wash. Styled in extraCss because it lives on a child. (native :hover on .cn-banner__dismiss)
                                dismissFocus .cn-banner__dismiss:focus-visibleKeyboard focus on the dismiss button shows the focus ring. Styled in extraCss. (native :focus-visible on .cn-banner__dismiss)
                                sticky[data-sticky]Sticks under the topbar while the page scrolls. Position only; styled in extraCss so examples stay static. (data-sticky attribute on the root)

                                Rules

                                • One banner per page at most. If a second notice appears, the more urgent one wins and the other waits.
                                • One sentence, ideally under 90 characters, with an optional <strong> lead. No title, no second line.
                                • One action at most (link or outline, size sm) plus the dismiss. Never a primary Button in a banner.
                                • No radius, no shadow, no margin: the banner touches the edges of its container and sits directly under the topbar.
                                • Use data-sticky only when the notice must stay visible while scrolling (an open incident). Sticky banners are never accent.
                                • variant="solid" only for tone danger or info. Never solid accent, warning or neutral.
                                • Danger banners for open incidents are not dismissible; they leave when the incident closes.
                                • Accent tone is for announcements only. It never signals a status or an error.

                                Accessibility

                                • role="status" by default; role="alert" for danger banners inserted after page load.
                                • The banner is inside the header landmark or the first child of <main>, so screen readers meet it before the page content.
                                • The dismiss button needs aria-label="Dismiss"; the action must be a real <button> or <a href>.
                                • Solid variants keep AA contrast: white text on info/danger, ink text on warning.
                                Feedback

                                Toast .cn-toastedit

                                Short-lived notification panel that slides in at the bottom-right corner: a raised white card with an icon, a title, one line of detail and at most one action. The component styles the panel; the fixed region that stacks toasts is a separate class.

                                Use: Use to confirm something the user just did ("Draft saved", "Invite sent") or to report a background event that needs no decision. Not for errors the user must act on (use Alert in place, or Dialog) and not for information that must persist (use Banner).

                                Neutral
                                Draft saved

                                Reply to Nakamura Trading Co. · 2 seconds ago

                                HTML
                                <div class="cn-toast" data-tone="neutral" data-state="open" role="status"><svg class="cn-toast__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg><div class="cn-toast__content"><div class="cn-toast__title">Draft saved</div><p class="cn-toast__description">Reply to Nakamura Trading Co. · 2 seconds ago</p></div><button type="button" class="cn-toast__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                Success with action
                                Invite sent to Hoffmann GmbH

                                They will receive it within a minute.

                                HTML
                                <div class="cn-toast" data-tone="success" data-state="open" role="status"><svg class="cn-toast__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><div class="cn-toast__content"><div class="cn-toast__title">Invite sent to Hoffmann GmbH</div><p class="cn-toast__description">They will receive it within a minute.</p><div class="cn-toast__action"><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">Undo</span></button></div></div><button type="button" class="cn-toast__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                Warning
                                4 prospects skipped

                                No verified email found. They stay in the list as “unreachable”.

                                HTML
                                <div class="cn-toast" data-tone="warning" data-state="open" role="status"><svg class="cn-toast__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 2.5l6 11H2l6-11zM8 7v3M8 11.5v.5"/></svg><div class="cn-toast__content"><div class="cn-toast__title">4 prospects skipped</div><p class="cn-toast__description">No verified email found. They stay in the list as “unreachable”.</p></div><button type="button" class="cn-toast__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                Danger (background failure)
                                Export failed

                                The file was not generated. Try again from the Export menu.

                                HTML
                                <div class="cn-toast" data-tone="danger" data-state="open" role="status"><svg class="cn-toast__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 2.5l6 11H2l6-11zM8 7v3M8 11.5v.5"/></svg><div class="cn-toast__content"><div class="cn-toast__title">Export failed</div><p class="cn-toast__description">The file was not generated. Try again from the Export menu.</p><div class="cn-toast__action"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Retry</span></button></div></div><button type="button" class="cn-toast__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                Info
                                Nightly refresh finished

                                37 new companies in Japan and South Korea.

                                HTML
                                <div class="cn-toast" data-tone="info" data-state="open" role="status"><svg class="cn-toast__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg><div class="cn-toast__content"><div class="cn-toast__title">Nightly refresh finished</div><p class="cn-toast__description">37 new companies in Japan and South Korea.</p><div class="cn-toast__action"><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">View</span></button></div></div><button type="button" class="cn-toast__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>

                                Recipes

                                Stack of two (static)In the app the same markup lives inside .cn-toast-region (fixed, bottom-right). Newest at the bottom.
                                Shortlist exported

                                prospects-japan.xlsx · 1.2 MB

                                Agent paused

                                Outreach resumes at 08:00 in the buyer’s timezone.

                                HTML
                                <div style="display:flex;flex-direction:column;gap:var(--cn-space-2)"><div class="cn-toast" data-tone="success" data-state="open" role="status"><svg class="cn-toast__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><div class="cn-toast__content"><div class="cn-toast__title">Shortlist exported</div><p class="cn-toast__description">prospects-japan.xlsx · 1.2 MB</p></div><button type="button" class="cn-toast__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div><div class="cn-toast" data-tone="neutral" data-state="open" role="status"><svg class="cn-toast__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg><div class="cn-toast__content"><div class="cn-toast__title">Agent paused</div><p class="cn-toast__description">Outreach resumes at 08:00 in the buyer’s timezone.</p></div><button type="button" class="cn-toast__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div></div>

                                Matrix

                                tone
                                neutral
                                Draft saved

                                Reply to Nakamura Trading Co. · 2 seconds ago

                                success
                                Draft saved

                                Reply to Nakamura Trading Co. · 2 seconds ago

                                warning
                                Draft saved

                                Reply to Nakamura Trading Co. · 2 seconds ago

                                danger
                                Draft saved

                                Reply to Nakamura Trading Co. · 2 seconds ago

                                info
                                Draft saved

                                Reply to Nakamura Trading Co. · 2 seconds ago

                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-toast <div> — The panel. 360px wide, raised surface, shadow-lg. role="status"; data-state="open|closed".
                                • .cn-toast__icon <svg> · optional — 16px icon in the tone color, top-aligned with the title. Decorative.
                                • .cn-toast__content <div> — Title + description + action. Flexes to fill.
                                • .cn-toast__title <div> — label-md, one line: what happened ("Invite sent").
                                • .cn-toast__description <p> · optional — body-sm muted, one line of detail (who, when, how many).
                                • .cn-toast__action <div> · optional — One link or ghost Button (size sm) under the description: "Undo", "View".
                                • .cn-toast__dismiss <button> · optional — 24px ghost icon button with an X in the top-right corner. aria-label="Dismiss".

                                Props

                                propvalues (default outlined)meaning
                                toneneutral success warning danger infoOnly the icon color changes; the panel stays white. neutral = plain confirmation; success = a completed action; warning = something to look at later; danger = a background failure that needs no immediate decision; info = a heads-up.

                                States

                                stateselectormeaning
                                open[data-state="open"]Expanded / visible. (data-state="open")
                                closed[data-state="closed"]Removed from view (display none) after the auto-dismiss timer or the dismiss button. (data-state="closed")
                                dismissHover .cn-toast__dismiss:hoverPointer over the dismiss button: subtle fill. Styled in extraCss because it lives on a child. (native :hover on .cn-toast__dismiss)
                                dismissFocus .cn-toast__dismiss:focus-visibleKeyboard focus on the dismiss button shows the focus ring. Styled in extraCss. (native :focus-visible on .cn-toast__dismiss)

                                Rules

                                • Toasts auto-dismiss after 5 seconds (8 seconds when they carry an action). Hovering pauses the timer.
                                • At most 3 toasts visible; the oldest leaves when a fourth arrives.
                                • Never use a toast for an error that needs a decision. If the user must act, show an Alert next to the thing or a Dialog.
                                • Title is 2–5 words in past tense ("Invite sent"); description is one line with the concrete object, count or time.
                                • At most one action, variant link or ghost, size sm. "Undo" is the most valuable action a toast can offer.
                                • Render toasts inside .cn-toast-region (fixed, bottom-right, z toast). The panel itself is never position: fixed.
                                • The panel stays white in every tone; only the icon changes color. No tinted toasts.
                                • Enter animation is translateY(8px) → 0 with opacity, 260ms; leaving is opacity only. Both disappear under prefers-reduced-motion.

                                Accessibility

                                • role="status" on the panel (polite); use aria-live="assertive" on the region only for danger toasts.
                                • The region is aria-label="Notifications"; toasts never steal focus when they appear.
                                • The dismiss button has aria-label="Dismiss"; Escape dismisses the most recent toast when a toast has focus.
                                • Pause the auto-dismiss timer while the toast or its action has focus or the pointer.
                                Feedback

                                Progress .cn-progressedit

                                Horizontal bar that shows how much of a known task is done: a muted track, a filled indicator whose width is the value, and an optional label/value line above. Indeterminate mode slides a short segment when the total is unknown.

                                Use: Use for tasks with a measurable share: an import, seats used of a plan, steps of an onboarding. Use the indeterminate state only briefly, while the total is being computed. For a wait without a value use Spinner; for loading content use Skeleton.

                                Default with label and value
                                Importing prospects42%
                                HTML
                                <div class="cn-progress" data-size="md" data-tone="action" role="progressbar" aria-labelledby="prg-import" aria-valuenow="42" aria-valuemin="0" aria-valuemax="100" style="max-width:360px"><div class="cn-progress__header"><span class="cn-progress__label" id="prg-import">Importing prospects</span><span class="cn-progress__value">42%</span></div><div class="cn-progress__track"><div class="cn-progress__indicator" style="width:42%"></div></div></div>
                                Large, accent (agent working)
                                Verifying companies118 / 212
                                HTML
                                <div class="cn-progress" data-size="lg" data-tone="accent" role="progressbar" aria-labelledby="prg-verify" aria-valuenow="118" aria-valuemin="0" aria-valuemax="212" style="max-width:360px"><div class="cn-progress__header"><span class="cn-progress__label" id="prg-verify">Verifying companies</span><span class="cn-progress__value">118 / 212</span></div><div class="cn-progress__track"><div class="cn-progress__indicator" style="width:56%"></div></div></div>
                                Warning near a limit
                                Seats used34 / 40
                                HTML
                                <div class="cn-progress" data-size="md" data-tone="warning" role="progressbar" aria-labelledby="prg-seats" aria-valuenow="34" aria-valuemin="0" aria-valuemax="40" style="max-width:360px"><div class="cn-progress__header"><span class="cn-progress__label" id="prg-seats">Seats used</span><span class="cn-progress__value">34 / 40</span></div><div class="cn-progress__track"><div class="cn-progress__indicator" style="width:85%"></div></div></div>
                                Success, complete
                                ExportDone
                                HTML
                                <div class="cn-progress" data-size="md" data-tone="success" role="progressbar" aria-labelledby="prg-done" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="max-width:360px"><div class="cn-progress__header"><span class="cn-progress__label" id="prg-done">Export</span><span class="cn-progress__value">Done</span></div><div class="cn-progress__track"><div class="cn-progress__indicator" style="width:100%"></div></div></div>
                                Small, no header (inside a row)
                                HTML
                                <div class="cn-progress" data-size="sm" data-tone="danger" role="progressbar" aria-label="Bounce rate" aria-valuenow="12" aria-valuemin="0" aria-valuemax="100" style="max-width:160px"><div class="cn-progress__track"><div class="cn-progress__indicator" style="width:12%"></div></div></div>
                                Indeterminate
                                Preparing the search…
                                HTML
                                <div class="cn-progress" data-size="md" data-tone="action" data-state="indeterminate" role="progressbar" aria-labelledby="prg-prep" aria-busy="true" style="max-width:360px"><div class="cn-progress__header"><span class="cn-progress__label" id="prg-prep">Preparing the search…</span></div><div class="cn-progress__track"><div class="cn-progress__indicator"></div></div></div>

                                Matrix

                                tone ↓ / size →smmdlg
                                action
                                Importing prospects42%
                                Importing prospects42%
                                Importing prospects42%
                                accent
                                Importing prospects42%
                                Importing prospects42%
                                Importing prospects42%
                                success
                                Importing prospects42%
                                Importing prospects42%
                                Importing prospects42%
                                warning
                                Importing prospects42%
                                Importing prospects42%
                                Importing prospects42%
                                danger
                                Importing prospects42%
                                Importing prospects42%
                                Importing prospects42%
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-progress <div> — Wrapper: header + track stacked with a 6px gap. role="progressbar" with aria-valuenow/min/max, or aria-busy in indeterminate mode.
                                • .cn-progress__header <div> · optional — Optional line above the track: label on the left, value on the right, body-xs muted.
                                • .cn-progress__label <span> · optional — What is being measured ("Importing prospects", "Seats used").
                                • .cn-progress__value <span> · optional — The number, tabular ("42%", "12 / 40").
                                • .cn-progress__track <div> — The full-width muted bar, radius full, clips the indicator.
                                • .cn-progress__indicator <div> — The filled part. Width comes from an inline style (style="width:42%") and transitions smoothly.

                                Props

                                propvalues (default outlined)meaning
                                sizesm md lgTrack height. sm = 2px for inside table rows and cards; md = 4px default; lg = 8px for a page-level task (import, onboarding).
                                toneaction accent success warning dangerFill color. action = default; accent = an agent is working on it; success = completed; warning = near a limit (≥ 80%); danger = over the limit or failed.

                                States

                                stateselectormeaning
                                indeterminate[data-state="indeterminate"]Total unknown: a 40%-wide segment slides across the track in a loop. No aria-valuenow; aria-busy="true". (data-state="indeterminate" on the root (omit the inline width))

                                Rules

                                • Width comes from the layout; the indicator width is the only inline style allowed (style="width:42%").
                                • Always show the value when the user can act on it (seats, quota). Show "Done" instead of 100% when the task is finished.
                                • Tone follows the value, not the brand: switch to warning at ≥ 80% of a limit and to danger when the limit is exceeded or the task failed.
                                • accent means an agent is doing the work. Do not use it for user-driven uploads.
                                • Indeterminate for at most a few seconds; if the total stays unknown, replace it with a Spinner and a sentence.
                                • Sizes: sm inside rows and cards, md by default, lg for one page-level task at a time.
                                • Never animate the indicator on first paint; only transitions between updates (260ms).

                                Accessibility

                                • role="progressbar" with aria-valuenow, aria-valuemin and aria-valuemax; use aria-valuetext when the number alone is unclear ("34 of 40 seats").
                                • Name it with aria-labelledby pointing to the label, or aria-label when there is no visible label.
                                • Indeterminate: omit aria-valuenow and set aria-busy="true" on the region being loaded.
                                • Color is never the only signal: the value text or the label states warning/danger conditions.
                                Feedback

                                Spinner .cn-spinneredit

                                Small rotating ring for a wait without a known duration. A 2px current-color circle with one transparent quarter, spinning at 0.7s; it inherits the text color so it fits inside buttons, rows and empty states.

                                Use: Use next to a short sentence while something loads and no layout is known yet ("Searching 212 companies…"), or inside a control that is busy. For loading content whose shape is known use Skeleton; for a measurable task use Progress. Never use more than one spinner in view.

                                Default (md, current color)
                                Loading
                                HTML
                                <span class="cn-spinner" data-size="md" data-tone="current" role="status"><span class="cn-sr-only">Loading</span></span>
                                Sizes
                                Loading Loading Loading Loading Loading
                                HTML
                                <span class="cn-spinner" data-size="xs" data-tone="current" role="status"><span class="cn-sr-only">Loading</span></span> <span class="cn-spinner" data-size="sm" data-tone="current" role="status"><span class="cn-sr-only">Loading</span></span> <span class="cn-spinner" data-size="md" data-tone="current" role="status"><span class="cn-sr-only">Loading</span></span> <span class="cn-spinner" data-size="lg" data-tone="current" role="status"><span class="cn-sr-only">Loading</span></span> <span class="cn-spinner" data-size="xl" data-tone="current" role="status"><span class="cn-sr-only">Loading</span></span>
                                Muted with a sentence
                                LoadingSearching 212 companies in Japan…
                                HTML
                                <div style="display:inline-flex;align-items:center;gap:var(--cn-space-2);color:var(--cn-color-fg-muted)"><span class="cn-spinner" data-size="sm" data-tone="muted" role="status"><span class="cn-sr-only">Loading</span></span><span class="cn-text-body-sm">Searching 212 companies in Japan…</span></div>
                                Action tone
                                Verifying
                                HTML
                                <span class="cn-spinner" data-size="lg" data-tone="action" role="status"><span class="cn-sr-only">Verifying</span></span>
                                On a filled action surfaceInside a real Button use data-loading instead; the Button draws its own spinner.
                                SendingSending…
                                HTML
                                <span style="display:inline-flex;align-items:center;gap:var(--cn-space-2);padding:var(--cn-space-2) var(--cn-space-3);border-radius:var(--cn-radius-md);background:var(--cn-color-bg-action);color:var(--cn-color-fg-on-action)"><span class="cn-spinner" data-size="sm" data-tone="on-action" role="status"><span class="cn-sr-only">Sending</span></span><span class="cn-text-label-md">Sending…</span></span>

                                Matrix

                                tone ↓ / size →xssmmdlgxl
                                currentLoadingLoadingLoadingLoadingLoading
                                mutedLoadingLoadingLoadingLoadingLoading
                                actionLoadingLoadingLoadingLoadingLoading
                                on-actionLoadingLoadingLoadingLoadingLoading
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-spinner <span> — The ring. role="status"; contains a visually hidden label (.cn-sr-only) so screen readers announce what is happening.

                                Props

                                propvalues (default outlined)meaning
                                sizexs sm md lg xlDiameter from the icon scale: xs 12px (inside badges), sm 14px (inside sm buttons and table rows), md 16px (default, inline with body text), lg 20px (empty states), xl 24px (page-level loading).
                                tonecurrent muted action on-actioncurrent = inherits the text color (default, inside buttons and rows); muted = subtle grey for quiet loading; action = the action color for a primary wait; on-action = the text color used on filled action surfaces.

                                Rules

                                • One spinner in view at a time. Several loading areas share one spinner at the top of the region, or use Skeleton.
                                • Pair it with a sentence that names what is happening ("Searching 212 companies…"); a lone spinner is only acceptable inside a control.
                                • Do not show it for waits under 300ms; flashing spinners feel slower than nothing.
                                • Inside a Button use data-loading; the Button already has its own spinner sized to the control.
                                • Size follows the text it sits next to: sm with body-sm, md with body-md, lg/xl only in empty states and page-level loading.
                                • Tone current by default; muted for background loading; action only for a primary wait the user asked for.
                                • Never place a spinner inside a Toast or a Badge.

                                Accessibility

                                • role="status" on the ring with a visually hidden text (.cn-sr-only) that says what is loading.
                                • When the wait ends, update the same live region with the result ("38 companies found") so the change is announced.
                                • The animation stops under prefers-reduced-motion (base.css); the static ring remains visible.
                                • Do not put the spinner inside aria-hidden containers when it is the only feedback.
                                Feedback

                                Skeleton .cn-skeletonedit

                                Grey placeholder shapes that hold the layout while content loads: text lines, a circle, a rectangle or a card, with a slow shimmer sweeping across. Same size as the content they stand in for, so nothing jumps when it arrives.

                                Use: Use when the shape of the incoming content is known (a list, a card, a profile header) and the wait is over ~300ms. For a wait with no known layout use Spinner; for a measurable task use Progress. Never leave skeletons on screen after an error; replace them with an empty state or an Alert.

                                Text linesConsecutive text skeletons space themselves 8px apart. The last line is shorter.
                                HTML
                                <div style="width:320px" aria-busy="true"><div class="cn-skeleton" data-variant="text" aria-hidden="true"></div><div class="cn-skeleton" data-variant="text" aria-hidden="true"></div><div class="cn-skeleton" data-variant="text" aria-hidden="true" style="width:60%"></div></div>
                                Circle
                                HTML
                                <div class="cn-skeleton" data-variant="circle" aria-hidden="true"></div>
                                Rect (image or control)
                                HTML
                                <div class="cn-skeleton" data-variant="rect" aria-hidden="true" style="width:240px;height:36px"></div>
                                Card
                                HTML
                                <div class="cn-skeleton" data-variant="card" aria-hidden="true" style="width:320px;height:140px"></div>

                                Recipes

                                List row (circle + two lines)Mirrors a prospect row: avatar, name, one line of meta. Repeat it 3–5 times, never more.
                                HTML
                                <div style="display:flex;align-items:center;gap:var(--cn-space-3);width:360px" aria-busy="true"><div class="cn-skeleton" data-variant="circle" aria-hidden="true"></div><div style="flex:1;min-width:0"><div class="cn-skeleton" data-variant="text" aria-hidden="true" style="width:45%"></div><div class="cn-skeleton" data-variant="text" aria-hidden="true" style="width:70%;height:0.85em"></div></div></div>
                                Card with header
                                HTML
                                <section class="cn-card" data-variant="default" data-padding="md" style="max-width:360px" aria-busy="true"><div class="cn-card__body"><div class="cn-skeleton" data-variant="text" aria-hidden="true" style="width:40%;height:1.25em"></div><div class="cn-skeleton" data-variant="text" aria-hidden="true" style="width:80%"></div><div class="cn-skeleton" data-variant="text" aria-hidden="true" style="width:65%"></div><div class="cn-skeleton" data-variant="rect" aria-hidden="true" style="width:120px;height:32px;margin-top:var(--cn-space-4)"></div></div></section>

                                Matrix

                                variant
                                text
                                circle
                                rect
                                card
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-skeleton <div> — One placeholder block. aria-hidden="true"; the region around it carries aria-busy. Size comes from the variant plus inline width/height.

                                Props

                                propvalues (default outlined)meaning
                                varianttext circle rect cardtext = one line, 1em tall, radius sm, full width (set width inline for the last line); circle = 40px avatar placeholder, radius full; rect = a box with radius md for images, inputs and buttons (size inline); card = radius xl, min-height 120px, for a whole card.

                                Rules

                                • Match the real content: same widths, same line count, same avatar size. A skeleton that does not match the layout causes a jump and is worse than a spinner.
                                • Show 3–5 rows for a list, never the whole page. Below the fold the real content can arrive silently.
                                • Do not show skeletons for waits under 300ms, and keep them at least 500ms once shown to avoid flicker.
                                • One shimmer rhythm for the whole page (1.4s); all skeletons animate in sync because they share one keyframe.
                                • Vary text widths (100%, 60%, 80%); identical lines look like a table, not text.
                                • Never put skeletons inside a Button, a Badge or a Toast.
                                • Errors replace skeletons with an Alert or an empty state; they never stay on screen.

                                Accessibility

                                • aria-hidden="true" on every skeleton; put aria-busy="true" on the region that is loading and remove it when content arrives.
                                • Announce the loaded state through a live region only when the wait was long (more than a few seconds).
                                • The shimmer stops under prefers-reduced-motion (base.css); the grey shapes remain.
                                Feedback

                                EmptyState .cn-empty-stateedit

                                What a region shows when it has nothing to show, in the reference layout: a modern gray featured icon (or a quiet illustration) above a short semibold title, one sentence of help capped at 352px and the actions that fill it. Centered, narrow, calm.

                                Use: Empty collections (no projects yet), empty filters and searches (no results), empty slots (no file uploaded) and drop zones. Not for errors (Alert) and not for loading (Skeleton).

                                No projects yet (default md)A 48px modern gray featured icon 20px above an 18px semibold title, 14px gray-600 text capped at 352px, two buttons 12px apart 32px below.

                                No projects yet

                                Projects keep your team's work in one place. Create the first one or import from another tool.

                                HTML
                                <div class="cn-empty-state" data-size="md" data-variant="default"><div class="cn-empty-state__header"><span class="cn-featured-icon cn-empty-state__icon" data-theme="modern" data-tone="gray" data-size="lg" data-shape="square"><svg class="cn-featured-icon__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg></span></div><h3 class="cn-empty-state__title">No projects yet</h3><p class="cn-empty-state__description">Projects keep your team's work in one place. Create the first one or import from another tool.</p><div class="cn-empty-state__actions"><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Import</span></button><button type="button" class="cn-button" data-variant="primary" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg><span class="cn-button__label">New project</span></button></div></div>
                                IllustrationAn inline SVG illustration (documents) in the header, 24px above the title.

                                You're all caught up

                                Every task assigned to you is done. New work will show up here as soon as a teammate assigns it.

                                HTML
                                <div class="cn-empty-state" data-size="md" data-variant="illustration"><div class="cn-empty-state__header"><svg class="cn-empty-state__illustration" viewBox="0 0 152 120" aria-hidden="true"><ellipse cx="76" cy="108" rx="60" ry="6" fill="var(--cn-color-bg-muted)"/><rect x="38" y="30" width="76" height="74" rx="8" fill="var(--cn-color-bg-surface)" stroke="var(--cn-color-border-control)" stroke-width="1.5"/><rect x="46" y="22" width="76" height="74" rx="8" fill="var(--cn-color-bg-surface)" stroke="var(--cn-color-border-control)" stroke-width="1.5"/><rect x="54" y="14" width="76" height="74" rx="8" fill="var(--cn-color-bg-surface)" stroke="var(--cn-color-border-control)" stroke-width="1.5"/><rect x="66" y="30" width="36" height="6" rx="3" fill="var(--cn-color-bg-muted)"/><rect x="66" y="44" width="52" height="6" rx="3" fill="var(--cn-color-bg-subtle)"/><rect x="66" y="58" width="44" height="6" rx="3" fill="var(--cn-color-bg-subtle)"/><circle cx="110" cy="80" r="14" fill="var(--cn-color-bg-action-subtle)" stroke="var(--cn-color-bg-action)" stroke-width="1.5"/><path d="M104.5 80.5l3.5 3.5 7-7.5" fill="none" stroke="var(--cn-color-bg-action)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div><h3 class="cn-empty-state__title">You're all caught up</h3><p class="cn-empty-state__description">Every task assigned to you is done. New work will show up here as soon as a teammate assigns it.</p><div class="cn-empty-state__actions"><button type="button" class="cn-button" data-variant="link-color" data-size="md"><span class="cn-button__label">View all tasks</span></button></div></div>
                                No results, with search (sm)The search field sits between the text and the actions so the query can be refined in place; 16px title.

                                No customers found

                                Your search “Osaka” did not match any customers. Try a different name or clear the filters.

                                HTML
                                <div class="cn-empty-state" data-size="sm" data-variant="with-search"><div class="cn-empty-state__header"><span class="cn-featured-icon cn-empty-state__icon" data-theme="modern" data-tone="gray" data-size="lg" data-shape="square"><svg class="cn-featured-icon__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg></span></div><h3 class="cn-empty-state__title">No customers found</h3><p class="cn-empty-state__description">Your search “Osaka” did not match any customers. Try a different name or clear the filters.</p><div class="cn-empty-state__search cn-input" data-variant="default" data-size="md"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search" aria-label="Search" value="Osaka"></div><div class="cn-empty-state__actions"><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Clear search</span></button><button type="button" class="cn-button" data-variant="primary" data-size="sm"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg><span class="cn-button__label">New customer</span></button></div></div>
                                Drop zone (dashed)Dashed gray-300 frame with a 40px modern icon; the button is the keyboard path.

                                Drop a CSV here

                                One customer per row, with at least a name and an email address.

                                HTML
                                <div class="cn-empty-state" data-size="md" data-variant="dashed" style="max-width:512px"><div class="cn-empty-state__header"><span class="cn-featured-icon cn-empty-state__icon" data-theme="modern" data-tone="gray" data-size="md" data-shape="square"><svg class="cn-featured-icon__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg></span></div><h3 class="cn-empty-state__title">Drop a CSV here</h3><p class="cn-empty-state__description">One customer per row, with at least a name and an email address.</p><div class="cn-empty-state__actions"><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Choose file</span></button></div></div>
                                Whole page (lg, card)56px icon, 20px title, 16px text; the white card frame when the empty state is the whole canvas region.

                                Nothing waiting for you

                                Your team is working on 12 tasks. You will see reviews and approvals here as soon as they need a decision.

                                HTML
                                <div class="cn-empty-state" data-size="lg" data-variant="card"><div class="cn-empty-state__header"><span class="cn-featured-icon cn-empty-state__icon" data-theme="modern" data-tone="gray" data-size="xl" data-shape="square"><svg class="cn-featured-icon__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg></span></div><h3 class="cn-empty-state__title">Nothing waiting for you</h3><p class="cn-empty-state__description">Your team is working on 12 tasks. You will see reviews and approvals here as soon as they need a decision.</p><div class="cn-empty-state__actions"><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">See what the team is doing</span></button></div></div>
                                Inline in a chart or table bodyThe bare 24px line icon for the smallest inline states, no header wrapper.

                                No results for “Osaka”

                                Try a wider region or clear the score filter.

                                HTML
                                <div class="cn-empty-state" data-size="sm" data-variant="default"><svg class="cn-empty-state__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><h3 class="cn-empty-state__title">No results for “Osaka”</h3><p class="cn-empty-state__description">Try a wider region or clear the score filter.</p><div class="cn-empty-state__actions"><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Clear filters</span></button></div></div>

                                Matrix

                                variant ↓ / size →smmdlg
                                default

                                No projects yet

                                Projects keep your team's work in one place. Create the first one or import from another tool.

                                No projects yet

                                Projects keep your team's work in one place. Create the first one or import from another tool.

                                No projects yet

                                Projects keep your team's work in one place. Create the first one or import from another tool.

                                card

                                No projects yet

                                Projects keep your team's work in one place. Create the first one or import from another tool.

                                No projects yet

                                Projects keep your team's work in one place. Create the first one or import from another tool.

                                No projects yet

                                Projects keep your team's work in one place. Create the first one or import from another tool.

                                dashed

                                No projects yet

                                Projects keep your team's work in one place. Create the first one or import from another tool.

                                No projects yet

                                Projects keep your team's work in one place. Create the first one or import from another tool.

                                No projects yet

                                Projects keep your team's work in one place. Create the first one or import from another tool.

                                illustration

                                No projects yet

                                Projects keep your team's work in one place. Create the first one or import from another tool.

                                No projects yet

                                Projects keep your team's work in one place. Create the first one or import from another tool.

                                No projects yet

                                Projects keep your team's work in one place. Create the first one or import from another tool.

                                with-search

                                No projects yet

                                Projects keep your team's work in one place. Create the first one or import from another tool.

                                No projects yet

                                Projects keep your team's work in one place. Create the first one or import from another tool.

                                No projects yet

                                Projects keep your team's work in one place. Create the first one or import from another tool.

                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-empty-state <div> — Centered column, max width 512px, padding from size. Plain, card or dashed frame.
                                • .cn-empty-state__header <div> · optional — Optional wrapper for the featured icon or illustration, 16 / 20 / 20px above the title (24 with an illustration).
                                • .cn-empty-state__icon <span> · optional — A FeaturedIcon (theme modern, tone gray, 48px; 56px at lg) that also carries this class; or a bare 24px line icon in fg-subtle for the smallest inline states. Decorative.
                                • .cn-empty-state__illustration <svg> · optional — An inline SVG illustration (≈152 × 120) drawn with token variables: documents, a cloud, a box, a credit card.
                                • .cn-empty-state__title <h3> — 16 / 18 / 20px semibold in ink: what is empty, in plain words ("No projects yet").
                                • .cn-empty-state__description <p> · optional — One sentence, 14px (16px at lg) fg-muted, max 352px: why it is empty or what will fill it.
                                • .cn-empty-state__search <div> · optional — Optional search Input (max 352px) between the text and the actions for "no results" states, so the user can refine the query in place.
                                • .cn-empty-state__actions <div> · optional — Row of one primary Button and optionally one secondary (outline) Button, 12px apart, 24 / 32 / 32px below the text.

                                Props

                                propvalues (default outlined)meaning
                                sizesm md lgsm = inline in a card, table body or chart (16px title, 14px text, 48px icon, 4px text gap); md = a page section (18px title, 14px text, 48px icon, 8px gap); lg = a whole empty page (20px title, 16px text, 56px icon).
                                variantdefault card dashed illustration with-searchdefault = no frame, a featured icon in the header (inside a Card, table or panel); card = white with a gray-200 ring, shadow-xs and radius 12 (standalone on the canvas); dashed = dashed gray-300 border, transparent, for drop zones and "create your first" slots; illustration = an inline SVG illustration in the header instead of the icon (24px below it); with-search = a search field under the text for "no results" states.

                                Rules

                                • Title says what is empty in the user's words ("No projects yet", "Nothing waiting for you"), never "No data", "Empty" or "404".
                                • Description is one sentence, 14px, capped at 352px, that says why or what will fill it; skip it when the title is enough.
                                • One primary action at most, and only if the user can fill the region from here; a second action is outline or link-color, 12px before it.
                                • The header holds one thing: a modern gray FeaturedIcon (48px, 56 at lg), an illustration, or a file icon. Never a colored disc, never an emoji.
                                • Filter-empty ("No results for X") is size sm with the with-search variant, keeps the filters visible and offers "Clear search"; it never uses a card or dashed frame.
                                • Use dashed only for drop zones and "create the first one" slots; card only when the empty state is the whole canvas region; illustration for onboarding moments, not for every empty table.
                                • Loading is not empty: show a Skeleton until the data arrives, then the EmptyState if it is truly empty.
                                • Do not center the empty state vertically in a tall page; keep it at the top with its padding so it reads like content.

                                Accessibility

                                • The title is a real heading at the level of the region it replaces (h2 for a page, h3 for a card); do not skip levels.
                                • The featured icon and illustration are aria-hidden; nothing in the empty state depends on them.
                                • When the empty state appears after a filter or search, announce it with aria-live="polite" on the region so the change is heard.
                                • Drop zones also work by keyboard: the "Choose file" Button opens the file picker; the dashed area is not the only way.
                                • A search field inside the empty state keeps the current query as its value and has an aria-label.
                                Feedback

                                InlineCta .cn-inline-ctaedit

                                A bordered row inside content that asks for one thing: a 40px featured icon or avatar, a label-md title, a body-sm description, a ghost secondary and a link or primary action, an optional progress bar and a × to dismiss. Tones tint the surface for brand, warning and success.

                                Use: Use between content blocks when the next step lives right there: upgrade to unlock a feature, verify an email before sending, free up storage, act on an invitation. For sidebar nudges use FeaturedCard; for page-wide messages use Banner; for form errors use Alert.

                                Upgrade to Pro (brand)A featured icon, two lines and a primary action on the action-subtle surface.
                                Upgrade to Pro

                                Unlimited dashboards, 30-day history and priority support for your whole team.

                                HTML
                                <div class="cn-inline-cta" data-variant="default" data-tone="brand" role="region" aria-label="Notice" style="width:100%;max-width:640px"><span class="cn-inline-cta__media"><span class="cn-featured-icon" data-theme="light" data-tone="brand" data-size="md" data-shape="square"><svg class="cn-featured-icon__icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 1.5l1.6 4.9 4.9 1.6-4.9 1.6L8 14.5l-1.6-4.9L1.5 8l4.9-1.6z"/></svg></span></span><div class="cn-inline-cta__content"><div class="cn-inline-cta__title">Upgrade to Pro</div><p class="cn-inline-cta__description">Unlimited dashboards, 30-day history and priority support for your whole team.</p><div class="cn-inline-cta__actions"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Dismiss</span></button><button type="button" class="cn-button" data-variant="primary" data-size="sm"><span class="cn-button__label">Upgrade plan</span></button></div></div><button type="button" class="cn-inline-cta__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                Storage with progressThe number is in the text; the bar shows it. Neutral surface for housekeeping.
                                Storage almost full

                                128 GB of 150 GB used. Uploads pause at 100%.

                                HTML
                                <div class="cn-inline-cta" data-variant="with-progress" data-tone="neutral" role="region" aria-label="Notice" style="width:100%;max-width:640px"><span class="cn-inline-cta__media"><span class="cn-featured-icon" data-theme="light" data-tone="gray" data-size="md" data-shape="square"><svg class="cn-featured-icon__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><ellipse cx="8" cy="4" rx="5" ry="2"/><path d="M3 4v8c0 1.1 2.25 2 5 2s5-.9 5-2V4M3 8c0 1.1 2.25 2 5 2s5-.9 5-2"/></svg></span></span><div class="cn-inline-cta__content"><div class="cn-inline-cta__title">Storage almost full</div><p class="cn-inline-cta__description">128 GB of 150 GB used. Uploads pause at 100%.</p><div class="cn-inline-cta__progress" role="progressbar" aria-label="Storage used" aria-valuemin="0" aria-valuemax="100" aria-valuenow="85"><div class="cn-inline-cta__progress-fill" style="width:85%"></div></div><div class="cn-inline-cta__actions"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Dismiss</span></button><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Manage storage</span></button></div></div><button type="button" class="cn-inline-cta__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                Verify your email (warning banner)One line on wide content: text left, actions right, × at the end.
                                Verify your email address

                                We sent a link to maya@lumen.co. It expires in 24 hours.

                                HTML
                                <div class="cn-inline-cta" data-variant="banner" data-tone="warning" role="region" aria-label="Notice" style="width:100%;max-width:640px"><span class="cn-inline-cta__media"><span class="cn-featured-icon" data-theme="light" data-tone="warning" data-size="md" data-shape="square"><svg class="cn-featured-icon__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="3.5" width="12" height="9" rx="1.5"/><path d="M2.5 4.5L8 8.5l5.5-4"/></svg></span></span><div class="cn-inline-cta__content"><div class="cn-inline-cta__title">Verify your email address</div><p class="cn-inline-cta__description">We sent a link to maya@lumen.co. It expires in 24 hours.</p></div><div class="cn-inline-cta__actions"><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">Resend link</span></button><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Open inbox</span></button></div><button type="button" class="cn-inline-cta__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                With avatar (success)A person's action shows their avatar instead of an icon.
                                Daniel Costa accepted your invite

                                He now has access to the Q3 launch plan workspace.

                                HTML
                                <div class="cn-inline-cta" data-variant="with-avatar" data-tone="success" role="region" aria-label="Notice" style="width:100%;max-width:640px"><span class="cn-inline-cta__media"><span class="cn-avatar" data-size="lg" data-shape="circle" data-tone="neutral" data-status="none" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span></span></span><div class="cn-inline-cta__content"><div class="cn-inline-cta__title">Daniel Costa accepted your invite</div><p class="cn-inline-cta__description">He now has access to the Q3 launch plan workspace.</p><div class="cn-inline-cta__actions"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Dismiss</span></button><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">View profile</span></button></div></div><button type="button" class="cn-inline-cta__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>

                                Matrix

                                variant ↓ / tone →neutralbrandwarningsuccess
                                default
                                Upgrade to Pro

                                Unlimited dashboards, 30-day history and priority support for your whole team.

                                Upgrade to Pro

                                Unlimited dashboards, 30-day history and priority support for your whole team.

                                Upgrade to Pro

                                Unlimited dashboards, 30-day history and priority support for your whole team.

                                Upgrade to Pro

                                Unlimited dashboards, 30-day history and priority support for your whole team.

                                with-progress
                                Upgrade to Pro

                                Unlimited dashboards, 30-day history and priority support for your whole team.

                                Upgrade to Pro

                                Unlimited dashboards, 30-day history and priority support for your whole team.

                                Upgrade to Pro

                                Unlimited dashboards, 30-day history and priority support for your whole team.

                                Upgrade to Pro

                                Unlimited dashboards, 30-day history and priority support for your whole team.

                                with-avatar
                                Upgrade to Pro

                                Unlimited dashboards, 30-day history and priority support for your whole team.

                                Upgrade to Pro

                                Unlimited dashboards, 30-day history and priority support for your whole team.

                                Upgrade to Pro

                                Unlimited dashboards, 30-day history and priority support for your whole team.

                                Upgrade to Pro

                                Unlimited dashboards, 30-day history and priority support for your whole team.

                                banner
                                Upgrade to Pro

                                Unlimited dashboards, 30-day history and priority support for your whole team.

                                Upgrade to Pro

                                Unlimited dashboards, 30-day history and priority support for your whole team.

                                Upgrade to Pro

                                Unlimited dashboards, 30-day history and priority support for your whole team.

                                Upgrade to Pro

                                Unlimited dashboards, 30-day history and priority support for your whole team.

                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-inline-cta <div> — The row: bg-surface, hairline, radius card, 16px padding, shadow-xs, flex with 16px gaps, relative for the ×.
                                • .cn-inline-cta__media <span> · optional — Wrapper for a 40px FeaturedIcon (system prompts) or a lg Avatar (a person's action).
                                • .cn-inline-cta__content <div> — Title, description and, in the stacked variants, the actions and progress.
                                • .cn-inline-cta__title <div> — label-md title, one line: what is offered or needed ("Verify your email address").
                                • .cn-inline-cta__description <p> — body-sm muted, one or two lines: why, and the concrete number when there is one.
                                • .cn-inline-cta__progress <div> · optional — 8px track (bg-muted, radius full) under the description for used-of-limit prompts. role="progressbar".
                                • .cn-inline-cta__progress-fill <div> · optional — The action-colored fill; width is the inline percentage.
                                • .cn-inline-cta__actions <div> — Row of sm Buttons: ghost secondary first, then one link-color or primary action. Inside the content, or beside it in the banner variant.
                                • .cn-inline-cta__dismiss <button> · optional — 28px button with a 20px × in the top-right corner (or at the end of the banner row). aria-label "Dismiss".

                                Props

                                propvalues (default outlined)meaning
                                variantdefault with-progress with-avatar bannerdefault = featured icon, text, actions under the text. with-progress = adds an 8px progress bar between the description and the actions (storage, seats, quota). with-avatar = a person's avatar instead of the icon, for invitations and requests from someone. banner = one line: text on the left, actions on the right, × at the end; for wide content areas.
                                toneneutral brand warning successneutral = white surface with a hairline, for housekeeping (storage, invites). brand = action-subtle surface for upgrades and new features. warning = warning-subtle surface for something that blocks a next step (verify, expiring). success = success-subtle surface for a completed action that has a follow-up (accepted, connected).

                                States

                                stateselectormeaning
                                dismissHover .cn-inline-cta__dismiss:hoverPointer over the × (extraCss): subtle fill, ink icon. (native :hover on .cn-inline-cta__dismiss)
                                dismissFocus .cn-inline-cta__dismiss:focus-visibleKeyboard focus on the × shows the focus ring (extraCss). (native :focus-visible on .cn-inline-cta__dismiss)

                                Rules

                                • One ask per row: the title names it, the description gives the reason and the number, the last action does it.
                                • At most two actions: a ghost sm secondary ("Dismiss", "Later") first and one link-color or primary sm action last. Primary only for the one conversion of the page.
                                • Featured icon for system prompts, avatar for a person's action; never both, never an image.
                                • Tone follows meaning: brand for upgrades and launches, warning for something that blocks the next step, success for a completed action with a follow-up, neutral for housekeeping.
                                • Progress appears only with a real limit and the used-of-total in the description; the fill stays action-colored, the text carries the warning.
                                • Use banner only when the row is wider than 640px; below that the actions wrap under the text (default).
                                • Every InlineCta is dismissible unless the ask blocks the page (then it is a Banner). Remember the dismissal per user.
                                • Place it next to what it talks about (above the upload list, under the email field), never at the top of every page.

                                Accessibility

                                • The row is a labelled region (role="region" + aria-label) or a plain <section>; use role="status" only when it appears in response to an action.
                                • Featured icons are aria-hidden; avatars with initials carry role="img" + aria-label with the person's name.
                                • The progress bar has role="progressbar" with aria-valuenow/min/max and an aria-label; the same number is in the description.
                                • The × has aria-label "Dismiss" and is the last Tab stop in the row; actions are real <button>s or links.
                                • Tone is never the only signal: the title says "Verify", "Upgrade" or "Accepted" in words.
                                Feedback

                                Notification .cn-notificationedit

                                An in-app notification card: a 400px raised white panel with an optional avatar or featured icon on the left, a semibold title, one or two lines of supporting text, a timestamp and up to two text actions. Unlike Toast it is not timed: it sits in a notification panel or feed until dismissed.

                                Use: Use in a notifications drawer, a bell-menu panel or an inbox list to tell a person something happened that they may act on later: a mention, a comment, an upload finishing, a product announcement. Not for transient confirmations (Toast), not for page-level warnings (Alert or Banner) and not for anything that needs a decision now (Dialog).

                                Default (text only)
                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                HTML
                                <div class="cn-notification" data-variant="default" data-tone="neutral" role="status"><div class="cn-notification__content"><div class="cn-notification__title">Your export is ready</div><p class="cn-notification__description">prospects-q3.csv · 1.2 MB · 2,418 rows</p><time class="cn-notification__time" datetime="2026-09-11T09:41">Just now</time></div><button type="button" class="cn-notification__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                With avatar and actionsSocial events show who acted. Two actions at most: a ghost Dismiss and one link action.
                                OR
                                Maya Chen commented on your post

                                “Looks great — can we ship the reporting tab on Monday instead of Friday?”

                                HTML
                                <div class="cn-notification" data-variant="with-avatar" data-tone="neutral" role="status"><span class="cn-notification__media" role="img" aria-label="Maya Chen">OR</span><div class="cn-notification__content"><div class="cn-notification__title">Maya Chen commented on your post</div><p class="cn-notification__description">“Looks great — can we ship the reporting tab on Monday instead of Friday?”</p><time class="cn-notification__time" datetime="2026-09-11T09:39">2 minutes ago</time><div class="cn-notification__actions"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Dismiss</span></button><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">Reply</span></button></div></div><button type="button" class="cn-notification__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                Featured icon, success tone
                                Payment received

                                Nakamura Trading Co. paid invoice INV-3066 · $12,400.00

                                HTML
                                <div class="cn-notification" data-variant="with-featured-icon" data-tone="success" role="status"><span class="cn-notification__media" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="10" cy="10" r="7.25"/><path d="M6.75 10.25l2.25 2.25 4.25-4.5"/></svg></span><div class="cn-notification__content"><div class="cn-notification__title">Payment received</div><p class="cn-notification__description">Nakamura Trading Co. paid invoice INV-3066 · $12,400.00</p><time class="cn-notification__time" datetime="2026-09-11T08:12">1 hour ago</time><div class="cn-notification__actions"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Dismiss</span></button><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">View invoice</span></button></div></div><button type="button" class="cn-notification__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                Upload in progressThe neutral featured icon is a hairline square ("modern"); toned icons become tinted circles.
                                Uploading design-system.fig

                                1.4 MB of 2.3 MB · about 20 seconds left

                                60%
                                HTML
                                <div class="cn-notification" data-variant="with-progress" data-tone="neutral" role="status"><span class="cn-notification__media" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6.5 13.5a3.25 3.25 0 01-.5-6.46A4.5 4.5 0 0114.6 8.2a2.75 2.75 0 01-.6 5.3M10 16V9.5M7.25 12.25L10 9.5l2.75 2.75"/></svg></span><div class="cn-notification__content"><div class="cn-notification__title">Uploading design-system.fig</div><p class="cn-notification__description">1.4 MB of 2.3 MB · about 20 seconds left</p><div class="cn-notification__progress"><div class="cn-notification__progress-track" role="progressbar" aria-label="Upload progress" aria-valuemin="0" aria-valuemax="100" aria-valuenow="60"><div class="cn-notification__progress-fill" style="width:60%"></div></div><span>60%</span></div></div><button type="button" class="cn-notification__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                With image (announcement)The image is an inline SVG placeholder here; in the app it is an <img> with alt text.
                                New feature: reporting

                                Build custom dashboards from any search and share them with your team.

                                HTML
                                <div class="cn-notification" data-variant="with-image" data-tone="neutral" role="status"><span class="cn-notification__media" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 2.75l1.9 5.35 5.35 1.9-5.35 1.9L10 17.25l-1.9-5.35-5.35-1.9 5.35-1.9z"/></svg></span><div class="cn-notification__content"><div class="cn-notification__title">New feature: reporting</div><p class="cn-notification__description">Build custom dashboards from any search and share them with your team.</p><time class="cn-notification__time" datetime="2026-09-10T17:00">Yesterday</time><svg class="cn-notification__image" viewBox="0 0 368 207" role="img" aria-label="Preview of the new reporting dashboard"><rect width="368" height="207" style="fill:var(--cn-color-bg-subtle)"/><rect x="24" y="24" width="140" height="10" rx="5" style="fill:var(--cn-color-border-strong)"/><rect x="24" y="44" width="88" height="8" rx="4" style="fill:var(--cn-color-border-default)"/><path d="M24 160 L80 128 L136 142 L192 96 L248 110 L304 64 L344 80" fill="none" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" style="stroke:var(--cn-color-bg-action)"/><path d="M24 183h320" stroke-width="1" style="stroke:var(--cn-color-border-default)"/></svg><div class="cn-notification__actions"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Dismiss</span></button><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">Learn more</span></button></div></div><button type="button" class="cn-notification__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                Warning with actions, unreaddata-unread adds the action-colored dot after the title.
                                Storage is 92% full

                                Uploads will pause at 100%. Free up space or upgrade your plan.

                                HTML
                                <div class="cn-notification" data-variant="with-actions" data-tone="warning" role="status" data-unread><span class="cn-notification__media" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 3.25l7.25 12.5H2.75L10 3.25zM10 8v3.5M10 14v.5"/></svg></span><div class="cn-notification__content"><div class="cn-notification__title">Storage is 92% full</div><p class="cn-notification__description">Uploads will pause at 100%. Free up space or upgrade your plan.</p><time class="cn-notification__time" datetime="2026-09-11T07:30">3 hours ago</time><div class="cn-notification__actions"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Later</span></button><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">Upgrade plan</span></button></div></div><button type="button" class="cn-notification__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>

                                Recipes

                                Danger tone, sync failedDanger notifications report a background failure and offer the fix as the link action; anything needing an immediate decision is a Dialog instead.
                                Pipedrive sync failed

                                14 deals were not updated. The token expired on Sep 9.

                                HTML
                                <div class="cn-notification" data-variant="with-featured-icon" data-tone="danger" role="status"><span class="cn-notification__media" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 3.25l7.25 12.5H2.75L10 3.25zM10 8v3.5M10 14v.5"/></svg></span><div class="cn-notification__content"><div class="cn-notification__title">Pipedrive sync failed</div><p class="cn-notification__description">14 deals were not updated. The token expired on Sep 9.</p><time class="cn-notification__time" datetime="2026-09-11T06:02">5 hours ago</time><div class="cn-notification__actions"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Dismiss</span></button><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">Reconnect</span></button></div></div><button type="button" class="cn-notification__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
                                Stack in a panelNewest first, 12px apart, inside a drawer or a popover anchored to the bell icon.
                                PB
                                Daniel Costa mentioned you in Q3 launch plan
                                LS
                                Sofia Almeida invited you to Brand refresh
                                HTML
                                <div style="display:flex;flex-direction:column;gap:var(--cn-space-3)"><div class="cn-notification" data-variant="with-avatar" data-tone="neutral" role="status"><span class="cn-notification__media" role="img" aria-label="Daniel Costa">PB</span><div class="cn-notification__content"><div class="cn-notification__title">Daniel Costa mentioned you in Q3 launch plan</div><time class="cn-notification__time" datetime="2026-09-11T09:20">20 minutes ago</time></div><button type="button" class="cn-notification__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div><div class="cn-notification" data-variant="with-avatar" data-tone="neutral" role="status"><span class="cn-notification__media" role="img" aria-label="Sofia Almeida">LS</span><div class="cn-notification__content"><div class="cn-notification__title">Sofia Almeida invited you to Brand refresh</div><time class="cn-notification__time" datetime="2026-09-11T08:55">45 minutes ago</time><div class="cn-notification__actions"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Decline</span></button><button type="button" class="cn-button" data-variant="link" data-size="sm"><span class="cn-button__label">Accept</span></button></div></div><button type="button" class="cn-notification__dismiss" aria-label="Dismiss"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div></div>

                                Matrix

                                variant ↓ / tone →neutralsuccesswarningdanger
                                default
                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                with-avatar
                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                with-featured-icon
                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                with-image
                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                with-progress
                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                with-actions
                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                Your export is ready

                                prospects-q3.csv · 1.2 MB · 2,418 rows

                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-notification <div> — The card. 400px wide (max 100%), raised surface, hairline, radius card, shadow-lg, padding 16. Flex row: media, content, then the absolutely-positioned dismiss button. role="status".
                                • .cn-notification__media <span> · optional — Optional 40px slot on the left: an avatar circle with initials (with-avatar) or a featured icon square/circle holding a 20px icon (with-featured-icon). Tone colors the featured icon.
                                • .cn-notification__content <div> — Title, description, time, optional progress, image and actions. Flexes to fill; keeps 32px of right padding clear for the dismiss button.
                                • .cn-notification__title <div> — label-md (14 semibold), one line: who did what ("Maya Chen commented on your post") or what happened ("Export ready").
                                • .cn-notification__description <p> · optional — body-md muted, one or two lines: the comment excerpt, the file name, the change summary.
                                • .cn-notification__time <time> · optional — body-sm subtle timestamp under the description ("2 minutes ago"). Use a <time datetime>.
                                • .cn-notification__progress <div> · optional — Optional upload/processing row: an 8px track with the action-colored fill plus a percentage label (label-sm muted). Set the fill width inline and mirror it in aria-valuenow.
                                • .cn-notification__progress-track <div> · optional — The 8px rounded track (bg-muted) inside the progress row. role="progressbar".
                                • .cn-notification__progress-fill <div> · optional — The filled portion of the track. Width is the percentage (inline style).
                                • .cn-notification__image <img> · optional — Optional full-width 16:9 image under the text (product announcements). Radius md, object-fit cover.
                                • .cn-notification__actions <div> · optional — Row of at most two Buttons: a ghost sm ("Dismiss", "Later") then a link sm ("View changes", "Reply").
                                • .cn-notification__dismiss <button> · optional — 36px ghost icon button with a 20px X, absolutely positioned in the top-right corner (8px inset). aria-label="Dismiss".

                                Props

                                propvalues (default outlined)meaning
                                variantdefault with-avatar with-featured-icon with-image with-progress with-actionsdefault = text only (title, description, time); with-avatar = a 40px avatar of the person who acted, for social events (comments, mentions, invites); with-featured-icon = a 40px featured icon for system events (payment, export, sync); with-image = a full-width 16:9 image below the text, for announcements; with-progress = a progress row for uploads and long jobs; with-actions = the text plus a Dismiss + primary-link action row (any variant may also carry actions).
                                toneneutral success warning dangerColors the featured icon only; the card stays white. neutral = informational (grey icon in a hairline square); success = something completed (green tinted circle); warning = needs attention soon (amber); danger = something failed (red). Avatars and images ignore the tone.

                                States

                                stateselectormeaning
                                dismissHover .cn-notification__dismiss:hoverPointer over the dismiss button: subtle fill, default ink. Styled in extraCss because it lives on a child. (native :hover on .cn-notification__dismiss)
                                dismissFocus .cn-notification__dismiss:focus-visibleKeyboard focus on the dismiss button shows the focus ring. Styled in extraCss. (native :focus-visible on .cn-notification__dismiss)
                                unread[data-unread]Not yet seen: the title stays semibold and a 8px action-colored dot sits after the title. Clear the attribute when the panel is opened. (data-unread on the root)

                                Rules

                                • Width is 400px (max 100%); the card never stretches to fill a wide panel. Panels and drawers are sized to the card, not the other way around.
                                • Title is one line, sentence case, no period: "{Person} {verb} {object}" for social events, "{Object} {state}" for system events.
                                • Description is at most two lines: a quoted excerpt, a file name with size, or the concrete change. Truncate long comments with an ellipsis and let the action open the full thing.
                                • Always show a timestamp (relative under 24h, absolute after) in a <time datetime>.
                                • At most two actions: a ghost sm secondary first ("Dismiss", "Later", "Decline") and one link sm primary last ("View changes", "Reply", "Accept").
                                • The card stays white in every tone; only the featured icon is tinted. Never tint the whole card or add a colored left bar.
                                • Avatar for people, featured icon for the system, image only for announcements. Never two media slots.
                                • Progress rows show a percentage and a plain-language remainder ("about 20 seconds left"); when the job ends the card becomes a success notification, it does not disappear.
                                • Unread state is a dot after the title plus semibold; clear it when the panel opens, never require a click per card.

                                Accessibility

                                • role="status" on the card so new notifications are announced politely; the panel that holds them is a region labelled "Notifications".
                                • The dismiss button has aria-label="Dismiss" and is the last Tab stop inside the card; Escape inside the panel dismisses the focused card.
                                • Avatars with initials carry role="img" and aria-label="Full name"; featured icons and images used as decoration are aria-hidden.
                                • Progress uses role="progressbar" with aria-valuenow/min/max on the track and the percentage as visible text.
                                • The unread dot is decorative; expose "unread" in the accessible name or a visually hidden word so screen readers get it too.
                                Feedback

                                ProgressCircle .cn-progress-circleedit

                                A ring that shows a share of a whole: a hairline track, a round-capped indicator in the action (or status) color, and the value centered inside with an optional caption. The half variant is a gauge that opens downward.

                                Use: Use for one headline percentage that deserves space: storage used, goal completion, a KPI on a dashboard card, an upload in a dialog. For inline or row-level progress use Progress (the bar); for a wait without a value use Spinner; for several metrics at once use ActivityGauge.

                                Default (md, action)stroke-dashoffset = 263.89 × (1 − 0.72) = 73.9.
                                72%
                                Active users
                                HTML
                                <div class="cn-progress-circle" data-size="md" data-variant="circle" data-tone="action" role="progressbar" aria-valuenow="72" aria-valuemin="0" aria-valuemax="100" aria-label="Active users: 72%"><svg viewBox="0 0 100 100" aria-hidden="true"><circle class="cn-progress-circle__track" cx="50" cy="50" r="42"/><circle class="cn-progress-circle__indicator" cx="50" cy="50" r="42" style="stroke-dashoffset:73.9"/></svg><div class="cn-progress-circle__label">72%</div><div class="cn-progress-circle__caption">Active users</div></div>
                                Sizes64 / 96 / 160 / 200px with label-xs / label-sm / heading-sm / heading-lg values.
                                40%
                                65%
                                Onboarding
                                72%
                                Active users
                                88%
                                Quarterly goal
                                HTML
                                <div style="display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--cn-space-6)"><div class="cn-progress-circle" data-size="xs" data-variant="circle" data-tone="action" role="progressbar" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" aria-label="Storage used: 40%"><svg viewBox="0 0 100 100" aria-hidden="true"><circle class="cn-progress-circle__track" cx="50" cy="50" r="42"/><circle class="cn-progress-circle__indicator" cx="50" cy="50" r="42" style="stroke-dashoffset:158.3"/></svg><div class="cn-progress-circle__label">40%</div></div><div class="cn-progress-circle" data-size="sm" data-variant="circle" data-tone="action" role="progressbar" aria-valuenow="65" aria-valuemin="0" aria-valuemax="100" aria-label="Onboarding: 65%"><svg viewBox="0 0 100 100" aria-hidden="true"><circle class="cn-progress-circle__track" cx="50" cy="50" r="42"/><circle class="cn-progress-circle__indicator" cx="50" cy="50" r="42" style="stroke-dashoffset:92.4"/></svg><div class="cn-progress-circle__label">65%</div><div class="cn-progress-circle__caption">Onboarding</div></div><div class="cn-progress-circle" data-size="md" data-variant="circle" data-tone="action" role="progressbar" aria-valuenow="72" aria-valuemin="0" aria-valuemax="100" aria-label="Active users: 72%"><svg viewBox="0 0 100 100" aria-hidden="true"><circle class="cn-progress-circle__track" cx="50" cy="50" r="42"/><circle class="cn-progress-circle__indicator" cx="50" cy="50" r="42" style="stroke-dashoffset:73.9"/></svg><div class="cn-progress-circle__label">72%</div><div class="cn-progress-circle__caption">Active users</div></div><div class="cn-progress-circle" data-size="lg" data-variant="circle" data-tone="action" role="progressbar" aria-valuenow="88" aria-valuemin="0" aria-valuemax="100" aria-label="Quarterly goal: 88%"><svg viewBox="0 0 100 100" aria-hidden="true"><circle class="cn-progress-circle__track" cx="50" cy="50" r="42"/><circle class="cn-progress-circle__indicator" cx="50" cy="50" r="42" style="stroke-dashoffset:31.7"/></svg><div class="cn-progress-circle__label">88%</div><div class="cn-progress-circle__caption">Quarterly goal</div></div></div>
                                Tonessuccess at completion, warning from 80%, danger over the limit.
                                Done
                                Import
                                82%
                                Seats used
                                96%
                                API quota
                                HTML
                                <div style="display:flex;flex-wrap:wrap;gap:var(--cn-space-6)"><div class="cn-progress-circle" data-size="sm" data-variant="circle" data-tone="success" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" aria-label="Import complete"><svg viewBox="0 0 100 100" aria-hidden="true"><circle class="cn-progress-circle__track" cx="50" cy="50" r="42"/><circle class="cn-progress-circle__indicator" cx="50" cy="50" r="42" style="stroke-dashoffset:0.0"/></svg><div class="cn-progress-circle__label">Done</div><div class="cn-progress-circle__caption">Import</div></div><div class="cn-progress-circle" data-size="sm" data-variant="circle" data-tone="warning" role="progressbar" aria-valuenow="82" aria-valuemin="0" aria-valuemax="100" aria-label="Seats used: 82%"><svg viewBox="0 0 100 100" aria-hidden="true"><circle class="cn-progress-circle__track" cx="50" cy="50" r="42"/><circle class="cn-progress-circle__indicator" cx="50" cy="50" r="42" style="stroke-dashoffset:47.5"/></svg><div class="cn-progress-circle__label">82%</div><div class="cn-progress-circle__caption">Seats used</div></div><div class="cn-progress-circle" data-size="sm" data-variant="circle" data-tone="danger" role="progressbar" aria-valuenow="96" aria-valuemin="0" aria-valuemax="100" aria-label="API quota: 96%"><svg viewBox="0 0 100 100" aria-hidden="true"><circle class="cn-progress-circle__track" cx="50" cy="50" r="42"/><circle class="cn-progress-circle__indicator" cx="50" cy="50" r="42" style="stroke-dashoffset:10.6"/></svg><div class="cn-progress-circle__label">96%</div><div class="cn-progress-circle__caption">API quota</div></div></div>
                                Half gaugeHalf the circumference: stroke-dashoffset = 131.95 × (1 − 0.65) = 46.2.
                                65%
                                Deal health
                                84%
                                Capacity
                                HTML
                                <div style="display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--cn-space-6)"><div class="cn-progress-circle" data-size="md" data-variant="half" data-tone="action" role="progressbar" aria-valuenow="65" aria-valuemin="0" aria-valuemax="100" aria-label="Deal health: 65%"><svg viewBox="0 0 100 100" aria-hidden="true"><circle class="cn-progress-circle__track" cx="50" cy="50" r="42"/><circle class="cn-progress-circle__indicator" cx="50" cy="50" r="42" style="stroke-dashoffset:46.2"/></svg><div class="cn-progress-circle__label">65%</div><div class="cn-progress-circle__caption">Deal health</div></div><div class="cn-progress-circle" data-size="sm" data-variant="half" data-tone="warning" role="progressbar" aria-valuenow="84" aria-valuemin="0" aria-valuemax="100" aria-label="Warehouse capacity: 84%"><svg viewBox="0 0 100 100" aria-hidden="true"><circle class="cn-progress-circle__track" cx="50" cy="50" r="42"/><circle class="cn-progress-circle__indicator" cx="50" cy="50" r="42" style="stroke-dashoffset:21.1"/></svg><div class="cn-progress-circle__label">84%</div><div class="cn-progress-circle__caption">Capacity</div></div></div>
                                Custom labelThe label can be a count instead of a percentage; aria-valuenow still carries the percentage.
                                118
                                of 212 verified
                                HTML
                                <div class="cn-progress-circle" data-size="md" data-variant="circle" data-tone="action" role="progressbar" aria-valuenow="56" aria-valuemin="0" aria-valuemax="100" aria-label="118 of 212 companies verified"><svg viewBox="0 0 100 100" aria-hidden="true"><circle class="cn-progress-circle__track" cx="50" cy="50" r="42"/><circle class="cn-progress-circle__indicator" cx="50" cy="50" r="42" style="stroke-dashoffset:116.1"/></svg><div class="cn-progress-circle__label">118</div><div class="cn-progress-circle__caption">of 212 verified</div></div>

                                Matrix

                                variant ↓ / size →xssmmdlg
                                circle
                                72%
                                Active users
                                72%
                                Active users
                                72%
                                Active users
                                72%
                                Active users
                                half
                                72%
                                Active users
                                72%
                                Active users
                                72%
                                Active users
                                72%
                                Active users
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-progress-circle <div> — Square box (inline-flex column, centered) that positions the svg behind the label. role="progressbar" with aria-valuenow/min/max and an aria-label. Carries data-size, data-variant, data-tone.
                                • .cn-progress-circle__track <circle> — The full ring in the hairline color (stroke border-default). r = 42 in a 0 0 100 100 viewBox, fill none.
                                • .cn-progress-circle__indicator <circle> — The value ring: same geometry, stroke in the tone color, round caps, dasharray 263.89. Its stroke-dashoffset is an inline style computed from the value.
                                • .cn-progress-circle__label <div> — The centered value ("72%"), tabular numerals. Type scales with size: label-xs / label-sm / heading-sm / heading-lg.
                                • .cn-progress-circle__caption <div> · optional — body-xs muted line under the value ("Active users"). Hidden on xs.

                                Props

                                propvalues (default outlined)meaning
                                sizexs sm md lgOuter box 64 / 96 / 160 / 200px. xs = inside table rows and list items (value only); sm = stat tiles; md = a dashboard card; lg = a page hero or an empty-state style focus.
                                variantcircle halfcircle = full ring, value in the middle; half = the top half of the ring as a gauge, value sitting on the chord. Use half when the number reads as a level (capacity, health) rather than completion.
                                toneaction success warning dangerIndicator color. action = default; success = completed or healthy; warning = near a limit (≥ 80%); danger = over the limit or failed.

                                Rules

                                • Geometry is fixed: viewBox 0 0 100 100, r = 42, circumference 263.89. Set the value only through the inline style on the indicator: stroke-dashoffset = 263.89 × (1 − value / 100); for the half variant 131.95 × (1 − value / 100).
                                • The label is HTML, never SVG text, so it uses the type scale and stays selectable; keep it to a number or a short word ("Done").
                                • Tone follows the value, not the brand: switch to warning at ≥ 80% of a limit and to danger when the limit is exceeded or the task failed; success only at 100% or "healthy".
                                • One ProgressCircle per card. Several percentages side by side are a table or an ActivityGauge.
                                • xs has no caption: put the meaning in the row label next to it.
                                • Never animate on first paint; transition only between value updates (260ms).
                                • Use half for levels (capacity, health, score) and circle for completion (progress, share of a goal).
                                • Stroke width scales with the size (10 / 8 / 6 / 5 viewBox units) so the ring stays around 6–10px at every size; do not override it.

                                Accessibility

                                • role="progressbar" with aria-valuenow, aria-valuemin="0", aria-valuemax="100" and an aria-label naming what is measured ("Active users: 72%").
                                • Use aria-valuetext when the visible label is not a percentage ("118 of 212 verified").
                                • The svg is aria-hidden; the label text is the visible equivalent of the ring, so color is never the only signal.
                                • Respect prefers-reduced-motion: the dashoffset transition is the only motion and can be disabled globally.
                                Overlays

                                Menu .cn-menuedit

                                The dropdown panel of the reference: a 248px raised white surface (radius 8, hairline ring, shadow-lg) of 36px items with 16px icons, right-aligned shortcuts, gray-200 separators, section labels, an optional account-card header and a destructive last item. Styles the panel only; positioning is a separate anchor layer.

                                Use: Use for a short list of actions on one object (row actions, "More" in a card header, the account menu) and for grouped option toggles (sort, view, switch account). Not for navigation (SidebarNav), not for picking a form value (Select / Combobox), not for anything that needs a search field (CommandPalette).

                                Actions on one object248px panel; verbs in sentence case, "…" when the item opens a dialog. Delete is last, after a separator, in red.
                                HTML
                                <div class="cn-menu" data-size="md" role="menu" aria-label="Project actions" data-state="open"><button type="button" role="menuitem" class="cn-menu__item"><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg><span class="cn-menu__text">View project</span><kbd class="cn-menu__shortcut">↵</kbd></button><button type="button" role="menuitem" class="cn-menu__item"><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg><span class="cn-menu__text">Edit</span><kbd class="cn-menu__shortcut">⌘E</kbd></button><button type="button" role="menuitem" class="cn-menu__item"><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-menu__text">Duplicate</span><kbd class="cn-menu__shortcut">⌘D</kbd></button><button type="button" role="menuitem" class="cn-menu__item"><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg><span class="cn-menu__text">Move to…</span></button><div class="cn-menu__separator" role="separator"></div><button type="button" role="menuitem" class="cn-menu__item" data-danger><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4.5h10M6.5 4.5V3h3v1.5M4.5 4.5l.6 8.5h5.8l.6-8.5"/></svg><span class="cn-menu__text">Delete</span><kbd class="cn-menu__shortcut">⌫</kbd></button></div>
                                Account menu with headerThe header is the account card (Avatar md + name + email); the "Switch account" section uses 20px avatars with the check on the right.
                                HTML
                                <div class="cn-menu" data-size="md" role="menu" aria-label="Account" data-state="open"><div class="cn-menu__header cn-avatar-label-group" data-size="md"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="online" role="img" aria-label="Maya Chen, online"><span class="cn-avatar__fallback" aria-hidden="true">MC</span><span class="cn-avatar__status"></span></span><div class="cn-avatar-label-group__text"><span class="cn-avatar-label-group__name">Maya Chen</span><span class="cn-avatar-label-group__email">maya@lumen.co</span></div></div><button type="button" role="menuitem" class="cn-menu__item"><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><span class="cn-menu__text">View profile</span><kbd class="cn-menu__shortcut">⌘K→P</kbd></button><button type="button" role="menuitem" class="cn-menu__item"><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg><span class="cn-menu__text">Settings</span><kbd class="cn-menu__shortcut">⌘S</kbd></button><button type="button" role="menuitem" class="cn-menu__item"><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg><span class="cn-menu__text">Keyboard shortcuts</span><kbd class="cn-menu__shortcut">?</kbd></button><div class="cn-menu__separator" role="separator"></div><div class="cn-menu__group" role="group" aria-labelledby="menu-switch"><div class="cn-menu__label" id="menu-switch">Switch account</div><div role="menuitemradio" tabindex="-1" aria-checked="true" class="cn-menu__item"><img class="cn-menu__avatar" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""><span class="cn-menu__text">Maya Chen</span><svg class="cn-menu__check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></div><div role="menuitemradio" tabindex="-1" aria-checked="false" class="cn-menu__item"><img class="cn-menu__avatar" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23CDCED1'/%3E%3Ccircle cx='32' cy='25' r='11' fill='%23FAFAFA'/%3E%3Cpath d='M10 64c2-13 11-21 22-21s20 8 22 21z' fill='%23FAFAFA'/%3E%3C/svg%3E" alt=""><span class="cn-menu__text">Daniel Costa</span><svg class="cn-menu__check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></div></div><div class="cn-menu__separator" role="separator"></div><button type="button" role="menuitem" class="cn-menu__item"><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg><span class="cn-menu__text">Log out</span><kbd class="cn-menu__shortcut">⌥⇧Q</kbd></button></div>
                                Grouped options with checksThe brand check occupies the leading slot even when hidden, so labels align.
                                HTML
                                <div class="cn-menu" data-size="md" role="menu" aria-label="View options" data-state="open"><div class="cn-menu__group" role="group" aria-labelledby="menu-sort"><div class="cn-menu__label" id="menu-sort">Sort by</div><div role="menuitemradio" tabindex="-1" aria-checked="true" class="cn-menu__item"><svg class="cn-menu__check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-menu__text">Newest first</span></div><div role="menuitemradio" tabindex="-1" aria-checked="false" class="cn-menu__item"><svg class="cn-menu__check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-menu__text">Oldest first</span></div><div role="menuitemradio" tabindex="-1" aria-checked="false" class="cn-menu__item"><svg class="cn-menu__check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-menu__text">Name</span></div></div><div class="cn-menu__separator" role="separator"></div><div class="cn-menu__group" role="group" aria-labelledby="menu-show"><div class="cn-menu__label" id="menu-show">Show</div><div role="menuitemcheckbox" tabindex="-1" aria-checked="true" class="cn-menu__item"><svg class="cn-menu__check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-menu__text">Archived projects</span></div><div role="menuitemcheckbox" tabindex="-1" aria-checked="false" class="cn-menu__item"><svg class="cn-menu__check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-menu__text">Only mine</span></div></div></div>
                                Submenu, highlighted and disabled itemsdata-highlighted is what keyboard navigation sets; a submenu item ends in a chevron; disabled items stay visible at 50%.
                                HTML
                                <div class="cn-menu" data-size="md" role="menu" aria-label="Row actions" data-state="open"><button type="button" role="menuitem" class="cn-menu__item" data-highlighted><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><span class="cn-menu__text">Assign to…</span></button><button type="button" role="menuitem" class="cn-menu__item" aria-haspopup="menu" aria-expanded="false"><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-menu__text">Share</span><svg class="cn-menu__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button><button type="button" role="menuitem" class="cn-menu__item"><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><span class="cn-menu__text">Snooze</span><kbd class="cn-menu__shortcut">S</kbd></button><button type="button" role="menuitem" class="cn-menu__item" aria-disabled="true"><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-menu__text">Mark as done</span><kbd class="cn-menu__shortcut">E</kbd></button></div>
                                Small32px items for dense tables.
                                HTML
                                <div class="cn-menu" data-size="sm" role="menu" aria-label="Cell actions" data-state="open"><button type="button" role="menuitem" class="cn-menu__item"><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-menu__text">Copy</span><kbd class="cn-menu__shortcut">⌘C</kbd></button><button type="button" role="menuitem" class="cn-menu__item"><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-menu__text">Paste</span><kbd class="cn-menu__shortcut">⌘V</kbd></button><div class="cn-menu__separator" role="separator"></div><button type="button" role="menuitem" class="cn-menu__item" data-danger><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4.5h10M6.5 4.5V3h3v1.5M4.5 4.5l.6 8.5h5.8l.6-8.5"/></svg><span class="cn-menu__text">Clear</span></button></div>
                                Closed (in the DOM, hidden)
                                data-state="closed" renders nothing.
                                HTML
                                <div class="cn-menu" data-size="md" role="menu" aria-label="Hidden menu" data-state="closed"><button type="button" role="menuitem" class="cn-menu__item"><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg><span class="cn-menu__text">You should not see this</span></button></div><span class="cn-text-body-sm">data-state="closed" renders nothing.</span>

                                Recipes

                                Anchored to a triggerWrap trigger + panel in .cn-menu-anchor; data-floating positions the panel 4px under the trigger. Add data-align="end" to right-align, data-side="top" to flip.
                                HTML
                                <div class="cn-menu-anchor" style="min-height:220px"><button type="button" class="cn-icon-button" data-variant="outline" data-size="md" data-shape="square" aria-label="More actions" aria-haspopup="menu" aria-expanded="true" aria-controls="menu-anchored" id="menu-anchored-trigger"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.25"/><circle cx="8" cy="8" r="1.25"/><circle cx="12.5" cy="8" r="1.25"/></svg></button><div class="cn-menu" data-size="md" role="menu" id="menu-anchored" aria-labelledby="menu-anchored-trigger" data-state="open" data-floating><button type="button" role="menuitem" class="cn-menu__item"><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg><span class="cn-menu__text">Rename</span></button><button type="button" role="menuitem" class="cn-menu__item"><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-menu__text">Duplicate</span><kbd class="cn-menu__shortcut">⌘D</kbd></button><div class="cn-menu__separator" role="separator"></div><button type="button" role="menuitem" class="cn-menu__item" data-danger><svg class="cn-menu__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4.5h10M6.5 4.5V3h3v1.5M4.5 4.5l.6 8.5h5.8l.6-8.5"/></svg><span class="cn-menu__text">Delete</span></button></div></div>

                                Matrix

                                size
                                sm
                                md
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-menu <div> — The panel: role="menu", 248px wide, bg-surface-raised, gray-200 ring, radius 8, shadow-lg, 4px vertical and 6px horizontal padding. data-state="open|closed".
                                • .cn-menu__header <div> · optional — Optional account card at the top: an Avatar md with the avatar label group (name + email), 12px padding, hairline below; bleeds to the panel edges.
                                • .cn-menu__group <div> · optional — role="group" wrapping related items, optionally labelled.
                                • .cn-menu__label <div> · optional — Section heading in 12px semibold fg-subtle, padding 6 × 10 (4 below): "Sort by", "Switch account". Referenced by the group's aria-labelledby.
                                • .cn-menu__item <button> — One action or option: role="menuitem" | "menuitemcheckbox" | "menuitemradio". 36px tall, padding 8 × 10, radius 6, 14px medium gray-700. Holds icon / avatar / check + text + shortcut or chevron.
                                • .cn-menu__icon <svg> · optional — 16px leading icon, fg-subtle. All items in a group have one, or none do.
                                • .cn-menu__avatar <img> · optional — 20px round avatar in the leading slot (account switchers, assignees).
                                • .cn-menu__check <svg> · optional — 16px brand check mark of checkbox / radio items, in the leading slot (or trailing after an avatar); invisible until aria-checked="true" so text stays aligned.
                                • .cn-menu__text <span> · optional — The item label; grows and truncates.
                                • .cn-menu__shortcut <kbd> · optional — Keyboard shortcut in 12px medium fg-subtle, pushed to the end. Only for shortcuts that really work.
                                • .cn-menu__chevron <svg> · optional — 16px chevron-right at the end of an item that opens a submenu.
                                • .cn-menu__separator <div> · optional — A 1px gray-200 rule bleeding to the panel edges, 4px above and below. role="separator".

                                Props

                                propvalues (default outlined)meaning
                                sizesm mdmd = 36px items, 14px medium; the reference. sm = 32px items (padding 6 × 8) for dense table row menus and toolbars.

                                States

                                stateselectormeaning
                                open[data-state="open"]Expanded / visible. (data-state="open")
                                closed[data-state="closed"]Hidden. The panel stays in the DOM for focus management but is display: none. (data-state="closed")
                                highlighted .cn-menu__item[data-highlighted], & .cn-menu__item:hoverThe item under the pointer or reached by arrow keys (on the ITEM): gray-50 fill, text one step darker. (data-highlighted on the item (keyboard) or native :hover)
                                checked .cn-menu__item[aria-checked="true"]A checkbox / radio item that is on: the brand check mark becomes visible. (aria-checked="true" on a role="menuitemcheckbox|menuitemradio" item)
                                danger .cn-menu__item[data-danger]Destructive action: fg-danger text and icon, danger-subtle fill when highlighted. Always last, after a separator. (data-danger on the item)
                                disabled .cn-menu__item[aria-disabled="true"]Not available now: 50% opacity, cursor not-allowed, no hover. Stays visible and focusable so the user learns it exists. (aria-disabled="true" on the item)

                                Rules

                                • Max 7 items per group and about 12 in total. Beyond that, the object has too many actions; move rare ones into a dialog or a settings page.
                                • Items are verbs in sentence case ("Duplicate", "Move to…"); "…" when the item opens a dialog. Options (sort, show) are nouns with checks.
                                • Items are 36px (sm 32) with 14px medium text; icons 16px fg-subtle, all-or-none within a group; shortcuts 12px on the right only when the key really works.
                                • Destructive actions are last, after a separator, marked data-danger, and always confirm.
                                • The panel is 248px wide (or wider to fit the trigger when the trigger is a full-width control); never narrower.
                                • Position with .cn-menu-anchor + data-floating (4px below the trigger, aligned to its start; data-align="end" to align to its end, data-side="top" to flip). One open menu at a time.
                                • The account header is the only place for an avatar label group inside a menu; items use 20px avatars.
                                • No submenus deeper than one level; the chevron item opens a second panel to the side.
                                • Disabled items stay visible with aria-disabled and explain themselves in a Tooltip; hiding them makes the menu feel unstable.
                                • The panel styles only; put backdrop, focus trap and scroll lock in the app layer, never in the component.

                                Accessibility

                                • Root has role="menu" and aria-labelledby pointing at the trigger (or aria-label). The trigger has aria-haspopup="menu", aria-expanded and aria-controls.
                                • Items are role="menuitem", "menuitemcheckbox" or "menuitemradio" with aria-checked; groups are role="group" with aria-labelledby; separators are role="separator".
                                • Keyboard: Down/Up move highlight (wrapping), Home/End jump, Enter/Space activate, Right opens a submenu, Esc closes and returns focus to the trigger, typing jumps to a matching item.
                                • Only one item is tabbable (roving tabindex); highlight is set with data-highlighted and reflected by aria-activedescendant or focus.
                                • Disabled items use aria-disabled="true" and remain focusable so they can be announced; never the disabled attribute.
                                • Avatars inside items are decorative (alt=""); the text names the person.
                                Overlays

                                CommandPalette .cn-command-paletteedit

                                The ⌘K menu of the reference: a 640px raised surface with a 56px search row on top, grouped results (40px options, or 64px stacked people rows with avatars) in the middle and a 52px key legend at the bottom. Styles the panel only; the fixed backdrop is a separate layer.

                                Use: Use as the one global launcher of the app: jump to any record, person or page and run any command by typing. Opened with ⌘K / Ctrl+K from anywhere and from the search field in the Topbar or Sidebar. Not a replacement for a page's search Input, not a Menu (no typing) and not a Select.

                                ActionsBefore typing: recent things, navigation with G-prefixed shortcuts, then commands. The first option is highlighted; a disabled command stays listed.
                                HTML
                                <div class="cn-command-palette" data-variant="default" role="dialog" aria-modal="true" aria-label="Command menu" data-state="open"><div class="cn-command-palette__search"><svg class="cn-command-palette__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-command-palette__input" type="text" role="combobox" aria-expanded="true" aria-controls="cp-list" aria-activedescendant="cp-a1" aria-autocomplete="list" aria-label="Search people, pages and commands" placeholder="Search…" value="" autocomplete="off" spellcheck="false"><kbd class="cn-command-palette__key">⌘K</kbd></div><div class="cn-command-palette__list" role="listbox" id="cp-list" aria-label="Suggestions"><div class="cn-command-palette__group" role="group" aria-labelledby="cp-g-recent"><div class="cn-command-palette__group-label" id="cp-g-recent" role="presentation">Recent</div><div role="option" id="cp-a1" class="cn-command-palette__item" aria-selected="true" data-highlighted><svg class="cn-command-palette__item-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-command-palette__item-label">Lumen website redesign</span><span class="cn-command-palette__item-meta">Project</span></div><div role="option" id="cp-a2" class="cn-command-palette__item" aria-selected="false"><svg class="cn-command-palette__item-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-command-palette__item-label">Q3 invoices</span><span class="cn-command-palette__item-meta">Folder</span></div></div><div class="cn-command-palette__group" role="group" aria-labelledby="cp-g-goto"><div class="cn-command-palette__group-label" id="cp-g-goto" role="presentation">Go to</div><div role="option" id="cp-a3" class="cn-command-palette__item" aria-selected="false"><svg class="cn-command-palette__item-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg><span class="cn-command-palette__item-label">Dashboard</span><kbd class="cn-command-palette__item-shortcut">G</kbd><kbd class="cn-command-palette__item-shortcut">H</kbd></div><div role="option" id="cp-a4" class="cn-command-palette__item" aria-selected="false"><svg class="cn-command-palette__item-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><span class="cn-command-palette__item-label">Calendar</span><kbd class="cn-command-palette__item-shortcut">G</kbd><kbd class="cn-command-palette__item-shortcut">C</kbd></div><div role="option" id="cp-a5" class="cn-command-palette__item" aria-selected="false"><svg class="cn-command-palette__item-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg><span class="cn-command-palette__item-label">Settings</span><kbd class="cn-command-palette__item-shortcut">G</kbd><kbd class="cn-command-palette__item-shortcut">S</kbd></div></div><div class="cn-command-palette__group" role="group" aria-labelledby="cp-g-cmd"><div class="cn-command-palette__group-label" id="cp-g-cmd" role="presentation">Commands</div><div role="option" id="cp-a6" class="cn-command-palette__item" aria-selected="false"><svg class="cn-command-palette__item-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg><span class="cn-command-palette__item-label">New project</span><kbd class="cn-command-palette__item-shortcut">⌘</kbd><kbd class="cn-command-palette__item-shortcut">N</kbd></div><div role="option" id="cp-a7" class="cn-command-palette__item" aria-selected="false"><svg class="cn-command-palette__item-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><span class="cn-command-palette__item-label">Invite teammate</span><kbd class="cn-command-palette__item-shortcut">⌘</kbd><kbd class="cn-command-palette__item-shortcut">I</kbd></div><div role="option" id="cp-a8" class="cn-command-palette__item" aria-selected="false" aria-disabled="true"><svg class="cn-command-palette__item-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-command-palette__item-label">Import CSV</span><span class="cn-command-palette__item-meta">Business plan</span></div></div></div><footer class="cn-command-palette__footer"><span><kbd class="cn-command-palette__key">↑</kbd><kbd class="cn-command-palette__key">↓</kbd>to navigate</span><span><kbd class="cn-command-palette__key">↵</kbd>to select</span><span><kbd class="cn-command-palette__key">esc</kbd>to close</span></footer></div>
                                People (stacked)data-variant="stacked": 64px rows with a 40px Avatar, the name over the handle.
                                HTML
                                <div class="cn-command-palette" data-variant="stacked" role="dialog" aria-modal="true" aria-label="Command menu" data-state="open"><div class="cn-command-palette__search"><svg class="cn-command-palette__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-command-palette__input" type="text" role="combobox" aria-expanded="true" aria-controls="cp-list-p" aria-activedescendant="cp-p1" aria-autocomplete="list" aria-label="Search people, pages and commands" placeholder="Search…" value="so" autocomplete="off" spellcheck="false"><kbd class="cn-command-palette__key">⌘K</kbd></div><div class="cn-command-palette__list" role="listbox" id="cp-list-p" aria-label="People"><div class="cn-command-palette__group" role="group" aria-labelledby="cp-g-people"><div class="cn-command-palette__group-label" id="cp-g-people" role="presentation">People</div><div role="option" id="cp-p1" class="cn-command-palette__item" aria-selected="true" data-highlighted><span class="cn-command-palette__item-avatar cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="online" aria-hidden="true"><span class="cn-avatar__fallback">SA</span><span class="cn-avatar__status"></span></span><span class="cn-command-palette__item-text"><span class="cn-command-palette__item-label">Sofia Almeida</span><span class="cn-command-palette__item-meta">@sofia · Design</span></span></div><div role="option" id="cp-p2" class="cn-command-palette__item" aria-selected="false"><span class="cn-command-palette__item-avatar cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="offline" aria-hidden="true"><span class="cn-avatar__fallback">TS</span><span class="cn-avatar__status"></span></span><span class="cn-command-palette__item-text"><span class="cn-command-palette__item-label">Tomás Silva</span><span class="cn-command-palette__item-meta">@tomas · Engineering</span></span></div><div role="option" id="cp-p3" class="cn-command-palette__item" aria-selected="false"><span class="cn-command-palette__item-avatar cn-avatar" data-size="md" data-shape="circle" data-tone="neutral" data-status="online" aria-hidden="true"><span class="cn-avatar__fallback">NB</span><span class="cn-avatar__status"></span></span><span class="cn-command-palette__item-text"><span class="cn-command-palette__item-label">Noah Berg</span><span class="cn-command-palette__item-meta">@noah · Product design</span></span></div></div></div><footer class="cn-command-palette__footer"><span><kbd class="cn-command-palette__key">↑</kbd><kbd class="cn-command-palette__key">↓</kbd>to navigate</span><span><kbd class="cn-command-palette__key">↵</kbd>to select</span><span><kbd class="cn-command-palette__key">esc</kbd>to close</span></footer></div>
                                Results for a queryMatching records first, then actions built from the query.
                                HTML
                                <div class="cn-command-palette" data-variant="default" role="dialog" aria-modal="true" aria-label="Command menu" data-state="open"><div class="cn-command-palette__search"><svg class="cn-command-palette__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-command-palette__input" type="text" role="combobox" aria-expanded="true" aria-controls="cp-list-r" aria-activedescendant="cp-r1" aria-autocomplete="list" aria-label="Search people, pages and commands" placeholder="Search…" value="inv" autocomplete="off" spellcheck="false"><kbd class="cn-command-palette__key">⌘K</kbd></div><div class="cn-command-palette__list" role="listbox" id="cp-list-r" aria-label="Results"><div class="cn-command-palette__group" role="group" aria-labelledby="cp-g-res"><div class="cn-command-palette__group-label" id="cp-g-res" role="presentation">Results</div><div role="option" id="cp-r1" class="cn-command-palette__item" aria-selected="true" data-highlighted><svg class="cn-command-palette__item-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-command-palette__item-label">INV-0042 · Aisha Khan</span><span class="cn-command-palette__item-meta">Invoice</span></div><div role="option" id="cp-r2" class="cn-command-palette__item" aria-selected="false"><svg class="cn-command-palette__item-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-command-palette__item-label">Invoices</span><span class="cn-command-palette__item-meta">Page</span></div></div><div class="cn-command-palette__group" role="group" aria-labelledby="cp-g-act"><div class="cn-command-palette__group-label" id="cp-g-act" role="presentation">Actions</div><div role="option" id="cp-r3" class="cn-command-palette__item" aria-selected="false"><svg class="cn-command-palette__item-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg><span class="cn-command-palette__item-label">Create invoice “inv”</span><kbd class="cn-command-palette__item-shortcut">⌘</kbd><kbd class="cn-command-palette__item-shortcut">N</kbd></div><div role="option" id="cp-r4" class="cn-command-palette__item" aria-selected="false"><svg class="cn-command-palette__item-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><span class="cn-command-palette__item-label">Search files for “inv”</span></div></div></div><footer class="cn-command-palette__footer"><span><kbd class="cn-command-palette__key">↑</kbd><kbd class="cn-command-palette__key">↓</kbd>to navigate</span><span><kbd class="cn-command-palette__key">↵</kbd>to select</span><span><kbd class="cn-command-palette__key">esc</kbd>to close</span></footer></div>
                                Empty (nothing matches)The query is echoed back; the footer stays.
                                HTML
                                <div class="cn-command-palette" data-variant="default" role="dialog" aria-modal="true" aria-label="Command menu" data-state="open"><div class="cn-command-palette__search"><svg class="cn-command-palette__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-command-palette__input" type="text" role="combobox" aria-expanded="true" aria-controls="cp-list-e" aria-activedescendant="" aria-autocomplete="list" aria-label="Search people, pages and commands" placeholder="Search…" value="zzzq" autocomplete="off" spellcheck="false"><kbd class="cn-command-palette__key">⌘K</kbd></div><div class="cn-command-palette__empty" role="status">No results for “zzzq”. Try a person, a page or a command.</div><footer class="cn-command-palette__footer"><span><kbd class="cn-command-palette__key">↑</kbd><kbd class="cn-command-palette__key">↓</kbd>to navigate</span><span><kbd class="cn-command-palette__key">↵</kbd>to select</span><span><kbd class="cn-command-palette__key">esc</kbd>to close</span></footer></div>

                                Matrix

                                variant
                                default
                                stacked
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-command-palette <div> — The panel: role="dialog" aria-modal, 640px wide (max 100%), bg-surface-raised, hairline ring, radius 16, shadow-xl. data-state="open|closed".
                                • .cn-command-palette__search <div> — The 56px search row with a hairline below: 20px search icon, bare input, ⌘K key chip.
                                • .cn-command-palette__icon <svg> — 20px search icon, fg-subtle, leading the input. Decorative.
                                • .cn-command-palette__input <input> — The bare text field, 16px, no border. role="combobox" controlling the list, aria-activedescendant pointing at the highlighted option.
                                • .cn-command-palette__list <div> — role="listbox": scrolling results (max 360px), 8px padding, groups of options.
                                • .cn-command-palette__group <div> · optional — role="group" with aria-labelledby its label.
                                • .cn-command-palette__group-label <div> · optional — Group heading in 12px medium fg-subtle, padding 8 × 8 (4 below): "Recent", "People", "Commands".
                                • .cn-command-palette__item <div> — role="option": a 40px row (64px in stacked), radius 6, padding 8 × 8, 14px medium; gray-50 when highlighted. Holds icon or avatar + label + optional meta + optional shortcut.
                                • .cn-command-palette__item-icon <svg> · optional — 16px leading icon, fg-subtle: what kind of thing this is (person, page, command).
                                • .cn-command-palette__item-avatar <span> · optional — A 32px Avatar (40px in stacked) in the leading slot of people rows, with its status dot.
                                • .cn-command-palette__item-text <span> · optional — Column of label + meta for stacked rows.
                                • .cn-command-palette__item-label <span> — The name of the thing or the command, 14px medium fg-default. Fills the row and truncates.
                                • .cn-command-palette__item-meta <span> · optional — 14px fg-muted context after (or under) the label: "@maya", "Settings".
                                • .cn-command-palette__item-shortcut <kbd> · optional — Key chip (12px medium fg-subtle, radius 4, gray-200 ring, padding 1 × 4) showing the global shortcut of a command; one chip per key.
                                • .cn-command-palette__empty <div> · optional — Centered muted message shown instead of the list when nothing matches; echoes the query.
                                • .cn-command-palette__footer <footer> — 52px legend on a hairline: ↑↓ to navigate · ↵ to select · esc to close.
                                • .cn-command-palette__key <kbd> — One key chip in the search row or footer: 12px medium fg-subtle on bg-surface with a gray-200 ring.

                                Props

                                propvalues (default outlined)meaning
                                variantdefault stackeddefault = 40px rows with the meta after the label (commands, pages, quick jumps). stacked = 64px rows with a 40px avatar and the meta under the label (people and records with two lines).

                                States

                                stateselectormeaning
                                open[data-state="open"]Expanded / visible. (data-state="open")
                                closed[data-state="closed"]Hidden. The panel is display: none; the backdrop layer is removed by the app. (data-state="closed")
                                highlighted .cn-command-palette__item[data-highlighted]The option the arrow keys are on (on the ITEM): gray-50 fill, darker icon. Exactly one, the first result by default. (data-highlighted on the option)
                                selected .cn-command-palette__item[aria-selected="true"]The option referenced by aria-activedescendant; same look as highlighted so pointer and keyboard agree. (aria-selected="true" on the option)
                                disabled .cn-command-palette__item[aria-disabled="true"]A command not available in this context: 50% opacity, no hover; still listed so it can be discovered. (aria-disabled="true" on the option)

                                Rules

                                • One palette per app, opened with ⌘K / Ctrl+K from anywhere and from the search field in the Topbar or Sidebar. It is the only global launcher.
                                • The panel is 640px wide with a 56px search row; options are 40px (stacked 64px) with 14px medium labels, 16px icons or 32 / 40px avatars, radius 6.
                                • The placeholder is short ("Search…") and the search row carries the ⌘K chip so people learn the shortcut.
                                • Group order is fixed: matching records first, then people, then navigation ("Go to"), then commands. Max 4 groups visible, 5–7 options each; the rest is reachable by typing.
                                • The first option is always highlighted so ↵ does something immediately. Highlight follows the pointer and the arrow keys, never both at once.
                                • Always render an empty state that echoes the query and suggests what to type; never an empty list.
                                • The footer legend is always visible; shortcuts on options appear only when they work globally, one key chip per key.
                                • Labels are the name of the thing (record title, page title, command verb); meta says what and where it is. No descriptions longer than a few words.
                                • Render inside .cn-command-palette-backdrop: fixed, overlay z-index, 6px blur, panel top-aligned at 15vh; never vertically centered.
                                • The panel is the component; open/close, focus trap, filtering and scroll lock live in the app layer.

                                Accessibility

                                • Root is role="dialog" aria-modal="true" with an aria-label; opening moves focus into the input, closing returns it to where the user was.
                                • The input is role="combobox" with aria-expanded, aria-controls (the listbox), aria-autocomplete="list" and aria-activedescendant pointing at the highlighted option.
                                • The list is role="listbox"; groups are role="group" aria-labelledby their label; options are role="option" with aria-selected on the highlighted one.
                                • Keyboard: ↑/↓ move the highlight (wrapping across groups), ↵ runs the option, Esc closes, typing filters; focus stays in the input the whole time.
                                • Announce result counts with an aria-live="polite" region ("4 results") and use role="status" on the empty state.
                                • Avatars in people rows are decorative (aria-hidden); the label names the person and the meta carries the status in words when it matters.
                                • Key chips are read naturally ("↑ ↓ to navigate"); do not rely on glyphs alone for commands with shortcuts.
                                Overlays

                                Tooltip .cn-tooltipedit

                                Small inverse-colored label that names or explains a control on hover and focus. One short sentence, a 6px arrow toward the trigger, nothing interactive inside.

                                Use: Use to name icon-only buttons and to add a hint to truncated values or abbreviations. Not for content the user needs to act on or copy (use Popover), not for long explanations (use helper text), and not as the only place a label lives on touch devices.

                                Top (default)
                                HTML
                                <div class="cn-tooltip" data-side="top" data-state="open" role="tooltip" id="tip-archive">Archive this prospect<span class="cn-tooltip__arrow"></span></div>
                                Bottom
                                HTML
                                <div class="cn-tooltip" data-side="bottom" data-state="open" role="tooltip" id="tip-refresh">Refreshed nightly at 02:00 UTC<span class="cn-tooltip__arrow"></span></div>
                                Left
                                HTML
                                <div class="cn-tooltip" data-side="left" data-state="open" role="tooltip" id="tip-verified">Verified with 3 public sources<span class="cn-tooltip__arrow"></span></div>
                                Right, wrapping at 240px
                                HTML
                                <div class="cn-tooltip" data-side="right" data-state="open" role="tooltip" id="tip-score">Fit score combines trade volume, product match and reply history.<span class="cn-tooltip__arrow"></span></div>

                                Recipes

                                Icon button with its tooltip (static)The trigger points to the tooltip with aria-describedby; the tooltip never contains the trigger’s label alone if the button has visible text.
                                HTML
                                <div style="display:inline-flex;flex-direction:column;align-items:center;gap:var(--cn-space-1)"><div class="cn-tooltip" data-side="top" data-state="open" role="tooltip" id="tip-copy">Copy company ID<span class="cn-tooltip__arrow"></span></div><button type="button" class="cn-button" data-variant="outline" data-size="sm" aria-describedby="tip-copy"><span class="cn-button__label">Copy</span></button></div>

                                Matrix

                                side
                                top
                                right
                                bottom
                                left
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-tooltip <div> — The panel. role="tooltip", id referenced by the trigger’s aria-describedby. Positioned by the app; the component styles it in place.
                                • .cn-tooltip__arrow <span> · optional — 6px square rotated 45°, same background as the panel, placed on the edge facing the trigger according to data-side.

                                Props

                                propvalues (default outlined)meaning
                                sidetop right bottom leftWhich side of the trigger the tooltip sits on; only moves the arrow. top is the default; use bottom under a topbar, left/right next to a sidebar.

                                States

                                stateselectormeaning
                                open[data-state="open"]Expanded / visible. (data-state="open")
                                closed[data-state="closed"]Hidden. The panel is removed from view (display none). (data-state="closed")

                                Rules

                                • Text only, at most one sentence, no period at the end unless it is two sentences (then it is too long).
                                • Never put links, buttons or inputs inside a tooltip. If it must be clicked, it is a Popover.
                                • Show after a 300ms hover delay and immediately on keyboard focus; hide on pointer leave, blur or Escape.
                                • Tooltips are not the only label on touch: every icon-only control also has aria-label, and mobile-only elements do not rely on tooltips at all.
                                • Prefer side="top". Use bottom under a fixed topbar and left/right for controls at the screen edge. Let the positioning library flip when there is no room.
                                • Max width 240px; wrapping is fine, scrolling is not.
                                • The panel is inverse-colored in both themes (dark on light, light on dark); never tone it.
                                • Do not show a tooltip that repeats the visible label of a text button.

                                Accessibility

                                • role="tooltip" on the panel and aria-describedby on the trigger pointing to its id.
                                • Opens on focus as well as hover; a control that can be hovered must be reachable by keyboard.
                                • Escape closes it and returns nothing else; focus never moves into the tooltip.
                                • Inverse colors keep AA contrast in both themes; body-xs is the minimum size, never smaller.
                                Overlays

                                Popover .cn-popoveredit

                                Small anchored panel with real content: a title, a short body, and at most two actions. Raised white surface, hairline, shadow-md and an optional arrow toward its trigger. The component styles the panel; the app positions it.

                                Use: Use for lightweight tasks next to their trigger: a quick filter, a date picker, a confirmation with one field, a preview card. Not for one-line hints (use Tooltip), not for flows that need the whole screen (use Dialog) and not for lists of commands (use Menu).

                                Default (md, bottom)
                                HTML
                                <div class="cn-popover" data-size="md" data-side="bottom" data-state="open" role="dialog" aria-labelledby="pop-filter-title"><span class="cn-popover__arrow"></span><header class="cn-popover__header"><div><h3 class="cn-popover__title" id="pop-filter-title">Filter by country</h3><p class="cn-popover__description">Prospects from these markets only.</p></div><button type="button" class="cn-popover__close" aria-label="Close"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></header><div class="cn-popover__body"><div class="cn-input" data-variant="default" data-size="sm"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search countries…" aria-label="Search countries"></div><p style="margin-top:var(--cn-space-2);color:var(--cn-color-fg-muted)">Japan, South Korea, Germany selected.</p></div><footer class="cn-popover__footer"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Clear</span></button><button type="button" class="cn-button" data-variant="primary" data-size="sm"><span class="cn-button__label">Apply</span></button></footer></div>
                                Small, top, no header
                                Fit score 82
                                Trade volume ↑ · Product match ↑ · Replied twice in 2025.
                                HTML
                                <div class="cn-popover" data-size="sm" data-side="top" data-state="open" role="region" aria-label="Fit score"><span class="cn-popover__arrow"></span><div class="cn-popover__body"><strong>Fit score 82</strong><br>Trade volume ↑ · Product match ↑ · Replied twice in 2025.</div></div>
                                Large preview, right
                                HTML
                                <div class="cn-popover" data-size="lg" data-side="right" data-state="open" role="dialog" aria-labelledby="pop-company-title"><span class="cn-popover__arrow"></span><header class="cn-popover__header"><div><h3 class="cn-popover__title" id="pop-company-title">Nakamura Trading Co.</h3><p class="cn-popover__description">Osaka, Japan · Importer of frozen seafood</p></div><button type="button" class="cn-popover__close" aria-label="Close"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></header><div class="cn-popover__body">14 years active · 2 decision makers found · last outreach 3 days ago. <span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm">Verified</span></div><footer class="cn-popover__footer"><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Open profile</span></button></footer></div>
                                Left, confirmation
                                HTML
                                <div class="cn-popover" data-size="md" data-side="left" data-state="open" role="dialog" aria-labelledby="pop-remove-title"><span class="cn-popover__arrow"></span><header class="cn-popover__header"><div><h3 class="cn-popover__title" id="pop-remove-title">Remove from shortlist?</h3><p class="cn-popover__description">You can add it back from the search results.</p></div></header><footer class="cn-popover__footer"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Keep</span></button><button type="button" class="cn-button" data-variant="danger" data-size="sm"><span class="cn-button__label">Remove</span></button></footer></div>

                                Matrix

                                size ↓ / side →toprightbottomleft
                                sm
                                md
                                lg
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-popover <div> — The panel. role="dialog" (or "region" when it is informational), data-state="open|closed", data-side for the arrow.
                                • .cn-popover__header <header> · optional — Title + description on the left, close button on the right. Optional when the body speaks for itself.
                                • .cn-popover__title <h3> · optional — heading-xs. Names the task ("Filter by country").
                                • .cn-popover__description <p> · optional — body-sm muted, one line under the title.
                                • .cn-popover__body <div> — body-sm content: a short form, a list, a preview.
                                • .cn-popover__footer <footer> · optional — Right-aligned row: ghost cancel then one primary Button, size sm.
                                • .cn-popover__close <button> · optional — 24px ghost icon button with an X at the top-right. aria-label="Close".
                                • .cn-popover__arrow <span> · optional — 8px rotated square with the same surface and hairline, on the edge facing the trigger.

                                Props

                                propvalues (default outlined)meaning
                                sizesm md lgPanel width. sm = 240px for a single control or a short list; md = 320px for a small form (default); lg = 400px for a preview or a two-column form.
                                sidetop right bottom leftWhich side of the trigger the panel opens on; moves the arrow and its border. bottom is the default for buttons in a toolbar.

                                States

                                stateselectormeaning
                                open[data-state="open"]Expanded / visible. (data-state="open")
                                closed[data-state="closed"]Hidden (display none). (data-state="closed")
                                closeHover .cn-popover__close:hoverPointer over the close button: subtle fill. Styled in extraCss because it lives on a child. (native :hover on .cn-popover__close)
                                closeFocus .cn-popover__close:focus-visibleKeyboard focus on the close button shows the focus ring. Styled in extraCss. (native :focus-visible on .cn-popover__close)

                                Rules

                                • One task per popover. If it needs more than one field group or a scrolling body, it is a Dialog or a Drawer.
                                • Width comes from size only; never set width inline. Height is content, max 60vh with an internal scroll on the body.
                                • Footer: ghost cancel first, then one primary (or danger) Button, both size sm. No footer for informational popovers.
                                • Opens on click (never on hover), closes on Escape, outside click, or the close button. The trigger gets aria-expanded.
                                • Position with a floating library; flip side when there is no room and keep the arrow centered on the trigger.
                                • Only one popover open at a time; opening another closes the first.
                                • Use the arrow when the popover is detached from its trigger by more than 4px; omit it when it hugs a toolbar.
                                • Enter animation: 4px slide + fade, 180ms; leave is fade only.

                                Accessibility

                                • role="dialog" with aria-labelledby on the title when the popover contains controls; role="region" with aria-label when it is read-only.
                                • Focus moves to the first control inside on open and returns to the trigger on close.
                                • The trigger carries aria-expanded and aria-controls pointing to the panel id.
                                • Escape closes; Tab cycles inside a dialog popover; the close button has aria-label="Close".
                                Overlays

                                Dialog .cn-dialogedit

                                Modal panel centered over a scrim for one decision or one short task. Raised white surface, radius-2xl, shadow-lg; header with a required title, a scrolling body, and a subtle footer where the single primary action sits last.

                                Use: Use when the user must decide or complete something before continuing: confirm a deletion, name a new search, pick between two plans. Not for details of a list item (use Drawer, which keeps the list visible), not for hints (Tooltip/Popover), and never as a container for a whole page of settings.

                                Default (md)
                                HTML
                                <div class="cn-dialog" data-size="md" data-variant="default" data-state="open" role="dialog" aria-modal="true" aria-labelledby="dlg-name-title"><header class="cn-dialog__header"><div><h2 class="cn-dialog__title" id="dlg-name-title">Name your shortlist</h2><p class="cn-dialog__description">You can rename it later from the list header.</p></div><button type="button" class="cn-dialog__close" aria-label="Close"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></header><div class="cn-dialog__body"><div class="cn-input" data-variant="default" data-size="md"><input class="cn-input__field" type="text" value="Japan · frozen seafood · Q4" aria-label="Shortlist name"></div></div><footer class="cn-dialog__footer"><button type="button" class="cn-button" data-variant="ghost" data-size="md"><span class="cn-button__label">Cancel</span></button><button type="button" class="cn-button" data-variant="primary" data-size="md"><span class="cn-button__label">Create shortlist</span></button></footer></div>
                                Destructive (sm)The only dialog that ends with a danger Button. Cancel stays first and ghost.
                                HTML
                                <div class="cn-dialog" data-size="sm" data-variant="destructive" data-state="open" role="dialog" aria-modal="true" aria-labelledby="dlg-delete-title"><header class="cn-dialog__header"><div style="display:flex;gap:var(--cn-space-3);align-items:flex-start"><span class="cn-dialog__icon"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4.5h10M6.5 4.5V3h3v1.5M4.5 4.5l.6 8.5h5.8l.6-8.5"/></svg></span><div><h2 class="cn-dialog__title" id="dlg-delete-title">Delete this search?</h2><p class="cn-dialog__description">“Germany · industrial pumps” and its 38 prospects will be removed. This cannot be undone.</p></div></div><button type="button" class="cn-dialog__close" aria-label="Close"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></header><footer class="cn-dialog__footer"><button type="button" class="cn-button" data-variant="ghost" data-size="md"><span class="cn-button__label">Cancel</span></button><button type="button" class="cn-button" data-variant="danger" data-size="md"><span class="cn-button__label">Delete search</span></button></footer></div>
                                With header icon (md)
                                HTML
                                <div class="cn-dialog" data-size="md" data-variant="default" data-state="open" role="dialog" aria-modal="true" aria-labelledby="dlg-pause-title"><header class="cn-dialog__header"><div style="display:flex;gap:var(--cn-space-3);align-items:flex-start"><span class="cn-dialog__icon"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg></span><div><h2 class="cn-dialog__title" id="dlg-pause-title">Pause outreach until Monday?</h2><p class="cn-dialog__description">Drafts stay ready; nothing is sent until 08:00 on 15 Sep in each buyer’s timezone.</p></div></div><button type="button" class="cn-dialog__close" aria-label="Close"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></header><footer class="cn-dialog__footer"><button type="button" class="cn-button" data-variant="ghost" data-size="md"><span class="cn-button__label">Keep sending</span></button><button type="button" class="cn-button" data-variant="primary" data-size="md"><span class="cn-button__label">Pause until Monday</span></button></footer></div>
                                Large with scrolling body (lg)max-height is inline here only to show the body scrolling; in the app it comes from calc(100vh - 64px).
                                HTML
                                <div class="cn-dialog" data-size="lg" data-variant="default" data-state="open" role="dialog" aria-modal="true" aria-labelledby="dlg-plan-title" style="max-height:360px"><header class="cn-dialog__header"><div><h2 class="cn-dialog__title" id="dlg-plan-title">Review the outreach plan</h2><p class="cn-dialog__description">12 companies · 3 waves · first wave tomorrow 09:00 JST</p></div><button type="button" class="cn-dialog__close" aria-label="Close"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></header><div class="cn-dialog__body"><p>Wave 1 (4 companies): Nakamura Trading Co., Hoffmann GmbH, Lagos Fresh Ltd., Marisco del Sur.</p><p style="margin-top:var(--cn-space-3)">Wave 2 (4 companies): Busan Cold Chain, Norsk Havfisk AS, Valencia Congelados, Pacific Reef Foods.</p><p style="margin-top:var(--cn-space-3)">Wave 3 (4 companies): Tokyo Maruha, Baltic Seafood OÜ, Dalian Ocean Trade, Cape Fisheries.</p><p style="margin-top:var(--cn-space-3)">Each company receives one email and one follow-up after 4 business days. Replies are drafted by the agent and wait for your approval.</p></div><footer class="cn-dialog__footer"><button type="button" class="cn-button" data-variant="ghost" data-size="md"><span class="cn-button__label">Back</span></button><button type="button" class="cn-button" data-variant="primary" data-size="md"><span class="cn-button__label">Start outreach</span></button></footer></div>

                                Matrix

                                variant ↓ / size →smmdlgxl
                                default
                                destructive
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-dialog <div> — The panel. role="dialog" aria-modal="true" aria-labelledby → title. data-state="open|closed". Rendered inside .cn-dialog-backdrop in the app.
                                • .cn-dialog__header <header> — Icon (optional) + title/description on the left, close button top-right. No bottom border.
                                • .cn-dialog__icon <span> · optional — 32px circle before the title: subtle fill and muted icon by default; danger fill and icon in variant="destructive".
                                • .cn-dialog__title <h2> — heading-md. Required. A question or a short verb phrase: "Delete this search?", "Name your shortlist".
                                • .cn-dialog__description <p> · optional — body-sm muted, one or two sentences with the consequence.
                                • .cn-dialog__close <button> · optional — 32px ghost icon button with an X in the top-right. aria-label="Close".
                                • .cn-dialog__body <div> — body-md content: a field, a short form, a summary. Scrolls when the panel hits max-height.
                                • .cn-dialog__footer <footer> — Subtle background, top hairline, right-aligned: ghost cancel first, then one primary (or danger) Button.

                                Props

                                propvalues (default outlined)meaning
                                sizesm md lg xlMax width of the panel. sm = 400px for a yes/no confirmation; md = 520px for one field or a short form (default); lg = 640px for a form with several fields; xl = 800px for a two-column layout or a preview. Pick two sizes per app and stick to them.
                                variantdefault destructivedefault = neutral header icon (or none); destructive = the header icon turns danger (red circle) and the footer’s primary action is a danger Button. Use only for irreversible actions.

                                States

                                stateselectormeaning
                                open[data-state="open"]Expanded / visible. (data-state="open")
                                closed[data-state="closed"]Hidden (display none). The backdrop is removed at the same time. (data-state="closed")
                                closeHover .cn-dialog__close:hoverPointer over the close button: subtle fill. Styled in extraCss because it lives on a child. (native :hover on .cn-dialog__close)
                                closeFocus .cn-dialog__close:focus-visibleKeyboard focus on the close button shows the focus ring. Styled in extraCss. (native :focus-visible on .cn-dialog__close)

                                Rules

                                • A title is required and is the accessible name. Write it as the question being answered or the task being done.
                                • Exactly one primary action, last in the footer. Cancel (or the escape hatch) comes first as a ghost Button, labelled with what it does ("Keep sending"), not "Cancel" when a better verb exists.
                                • variant="destructive": header icon in danger colors and the primary becomes a danger Button. Never two filled buttons.
                                • Render the panel inside .cn-dialog-backdrop (fixed, scrim, centered, z modal). The panel itself is never position: fixed.
                                • Escape and the backdrop click close the dialog unless the body has unsaved input; then ask.
                                • Pick at most two sizes per app (typically sm for confirmations and md for forms). xl is for previews only.
                                • The body scrolls; header and footer stay put. Never let the whole panel exceed the viewport.
                                • Do not stack dialogs. A dialog that needs another step becomes a stepper inside the same dialog or a full page.
                                • Enter: scale 0.98 → 1 with fade, 260ms; the scrim fades in at the same time.

                                Accessibility

                                • role="dialog" aria-modal="true" aria-labelledby pointing to the title (and aria-describedby to the description when present).
                                • Focus is trapped inside while open; on open it goes to the first control (or the cancel button for destructive dialogs) and returns to the trigger on close.
                                • Escape closes. The close button has aria-label="Close" and is the last element in the header.
                                • Everything behind the dialog gets inert (or aria-hidden) while it is open; body scroll is locked.
                                Overlays

                                Drawer .cn-draweredit

                                Side panel that slides in over the page for the detail of one item while the list stays visible behind the scrim. Raised surface, a hairline on the inner edge, shadow-lg; sticky header and footer with a scrolling body.

                                Use: Use for the detail of a list or table row (a prospect, a conversation, an agent run), for editing one record, and for filters on mobile. Not for confirmations (use Dialog) and never as a replacement for a page: if the content has its own navigation, it is a page.

                                Right, md (item detail)height:420px is inline only so the gallery shows the bounded panel; in the app it fills the backdrop.
                                HTML
                                <aside class="cn-drawer" data-side="right" data-size="md" data-state="open" role="dialog" aria-modal="true" aria-labelledby="drw-nakamura" style="height:420px"><header class="cn-drawer__header"><div><h2 class="cn-drawer__title" id="drw-nakamura">Nakamura Trading Co.</h2><p class="cn-drawer__description">Osaka, Japan · Importer of frozen seafood · <span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm">Verified</span></p></div><button type="button" class="cn-drawer__close" aria-label="Close"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></header><div class="cn-drawer__body"><p><strong>Why it fits</strong><br>Imports 1,200 t/year of frozen shrimp from Ecuador and Vietnam; two decision makers found; replied to a similar offer in 2025.</p><p style="margin-top:var(--cn-space-4)"><strong>Last activity</strong><br>Email opened 3 days ago · No reply yet · Follow-up scheduled for Tuesday 09:00 JST.</p><p style="margin-top:var(--cn-space-4)"><strong>Contacts</strong><br>Kenji Nakamura, Purchasing Director · Aiko Sato, Import Coordinator.</p></div><footer class="cn-drawer__footer"><button type="button" class="cn-button" data-variant="ghost" data-size="md"><span class="cn-button__label">Archive</span></button><button type="button" class="cn-button" data-variant="primary" data-size="md"><span class="cn-button__label">Send follow-up now</span></button></footer></aside>
                                Left, sm (filters)
                                HTML
                                <aside class="cn-drawer" data-side="left" data-size="sm" data-state="open" role="dialog" aria-modal="true" aria-labelledby="drw-filters" style="height:360px"><header class="cn-drawer__header"><div><h2 class="cn-drawer__title" id="drw-filters">Filters</h2><p class="cn-drawer__description">38 of 214 prospects match</p></div><button type="button" class="cn-drawer__close" aria-label="Close"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></header><div class="cn-drawer__body"><div class="cn-input" data-variant="default" data-size="sm"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Country or product…" aria-label="Filter prospects"></div><p style="margin-top:var(--cn-space-4);color:var(--cn-color-fg-muted)">Japan · South Korea · Frozen seafood · Replied</p></div><footer class="cn-drawer__footer"><button type="button" class="cn-button" data-variant="ghost" data-size="md"><span class="cn-button__label">Clear all</span></button><button type="button" class="cn-button" data-variant="primary" data-size="md"><span class="cn-button__label">Show 38</span></button></footer></aside>
                                Bottom sheet (mobile)Bottom drawers are full width, 80vh tall, with rounded top corners. The wrapper is inline here only to bound the example.
                                HTML
                                <div style="width:100%;max-width:420px"><aside class="cn-drawer" data-side="bottom" data-size="md" data-state="open" role="dialog" aria-modal="true" aria-labelledby="drw-sheet" style="height:300px"><header class="cn-drawer__header"><div><h2 class="cn-drawer__title" id="drw-sheet">Approve this reply?</h2><p class="cn-drawer__description">Drafted by the agent 2 minutes ago</p></div><button type="button" class="cn-drawer__close" aria-label="Close"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></header><div class="cn-drawer__body">“Thank you for your interest, Nakamura-san. We can supply 40 t of frozen shrimp per month from October, CIF Osaka. Would a call on Thursday suit you?”</div><footer class="cn-drawer__footer"><button type="button" class="cn-button" data-variant="ghost" data-size="md"><span class="cn-button__label">Edit</span></button><button type="button" class="cn-button" data-variant="primary" data-size="md"><span class="cn-button__label">Approve and send</span></button></footer></aside></div>
                                Large, read-only (no footer)
                                HTML
                                <aside class="cn-drawer" data-side="right" data-size="lg" data-state="open" role="dialog" aria-modal="true" aria-labelledby="drw-run" style="height:360px"><header class="cn-drawer__header"><div><h2 class="cn-drawer__title" id="drw-run">Agent run · 11 Sep, 09:14</h2><p class="cn-drawer__description">Prospecting · Japan · frozen seafood · 4 min 12 s</p></div><button type="button" class="cn-drawer__close" aria-label="Close"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></header><div class="cn-drawer__body"><p>Read 3 replies from Nakamura, Hoffmann and Lagos Fresh. Drafted 3 answers. Flagged Hoffmann as “asks for a price list” — waiting for your file.</p><p style="margin-top:var(--cn-space-4)">Searched 212 companies, verified 38, discarded 174 (no trade data or wrong product).</p></div></aside>

                                Matrix

                                side ↓ / size →smmdlg
                                right
                                left
                                bottom
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-drawer <aside> — The panel. role="dialog" aria-modal="true" aria-labelledby → title. data-state="open|closed", data-side, data-size. Rendered inside .cn-drawer-backdrop in the app; in the gallery it is shown bounded with an inline height.
                                • .cn-drawer__header <header> — Title/description on the left, close button on the right. Bottom hairline. Does not scroll.
                                • .cn-drawer__title <h2> — heading-md. Names the item ("Nakamura Trading Co.") or the task ("Edit agent").
                                • .cn-drawer__description <p> · optional — body-sm muted, one line of context under the title.
                                • .cn-drawer__close <button> — 32px ghost icon button with an X in the header. aria-label="Close".
                                • .cn-drawer__body <div> — The scrolling area. Padding space-6; content is sections, description lists, a timeline, a form.
                                • .cn-drawer__footer <footer> · optional — Subtle background, top hairline, right-aligned actions: ghost first, one primary last. Only when the drawer edits something.

                                Props

                                propvalues (default outlined)meaning
                                sideright left bottomEdge the drawer slides in from. right = detail of a list item (default, the Vera pattern); left = navigation or filters; bottom = a sheet on mobile: full width, 80vh tall, rounded top corners.
                                sizesm md lgWidth for right/left drawers. sm = 360px for a short detail or filters; md = 480px for the detail of a record (default); lg = 640px for a conversation or an editor. Ignored for side="bottom".

                                States

                                stateselectormeaning
                                open[data-state="open"]Expanded / visible. (data-state="open")
                                closed[data-state="closed"]Hidden (display none); the backdrop is removed at the same time. (data-state="closed")
                                closeHover .cn-drawer__close:hoverPointer over the close button: subtle fill. Styled in extraCss because it lives on a child. (native :hover on .cn-drawer__close)
                                closeFocus .cn-drawer__close:focus-visibleKeyboard focus on the close button shows the focus ring. Styled in extraCss. (native :focus-visible on .cn-drawer__close)

                                Rules

                                • The detail of a list item opens in a drawer, never in a new page. The list stays visible behind the scrim so the user keeps their place.
                                • One level deep only: a drawer never opens another drawer. A link inside a drawer that needs more space closes it and navigates.
                                • Render the panel inside .cn-drawer-backdrop with the same data-side; the panel itself is never position: fixed.
                                • Header is sticky and carries the item name; the body scrolls; the footer appears only when the drawer edits something (ghost first, one primary last).
                                • Width comes from size; never set it inline. Below 640px every drawer becomes full width (right/left) or a bottom sheet.
                                • Close on Escape, on the close button and on scrim click, unless the body has unsaved edits; then ask.
                                • Deep-link the open drawer (?prospect=123) so refresh and share keep it open.
                                • Enter: 16px slide from its side with fade, 260ms; leave is the reverse at 180ms.

                                Accessibility

                                • role="dialog" aria-modal="true" with aria-labelledby on the title.
                                • Focus moves to the close button (read-only drawers) or the first field (edit drawers) on open, and returns to the row that opened it on close.
                                • Focus is trapped inside while open; the page behind is inert.
                                • The close button has aria-label="Close"; Escape closes.
                                Layout

                                Card .cn-cardedit

                                A bounded surface for one thing: an entity, a form section, a stat. A neutral surface, a fine outline and compact corners, with a subtle fill on selected cards.

                                Use: Group related content that stands alone (a customer, a settings section, a KPI). Do not use cards as a layout grid for everything; lists and tables are better for collections of similar rows. Use variant="interactive" only when the whole card is one link/action. Put a CardHeader first when the card needs a title with actions.

                                Default with header and footerComponent previewFlat surface, fine outline, 24px padding; the footer sits on a hairline.

                                Lumen website redesign

                                Marketing site · Due Sep 19

                                12 tasks, 4 in review. Sofia Almeida owns the design track, Daniel Costa the build.

                                Updated 2h ago by Maya Chen
                                HTML
                                <section class="cn-card" data-variant="default" data-padding="md" style="max-width:400px"><header class="cn-card__header"><div><h3 class="cn-card__title">Lumen website redesign</h3><p class="cn-card__description">Marketing site · Due Sep 19</p></div><div class="cn-card__actions"><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="More actions"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.25"/><circle cx="8" cy="8" r="1.25"/><circle cx="12.5" cy="8" r="1.25"/></svg></button></div></header><div class="cn-card__body"><p class="cn-text-body-md">12 tasks, 4 in review. Sofia Almeida owns the design track, Daniel Costa the build.</p></div><footer class="cn-card__footer">Updated 2h ago by Maya Chen</footer></section>
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-card <section> — The surface: surface fill, fine outline, token-defined corners; padding via the padding prop.
                                • .cn-card__header <header> · optional — Title row with optional actions on the right, inside the padding (for a full-width hairline header use CardHeader with padding none).
                                • .cn-card__title <h3> · optional — heading-sm (18/28 semibold). One per card.
                                • .cn-card__description <p> · optional — 14px fg-muted, under the title.
                                • .cn-card__actions <div> · optional — Right-aligned row of ghost / outline buttons in the header, 12px apart.
                                • .cn-card__body <div> — Main content.
                                • .cn-card__footer <footer> · optional — Meta or actions on a gray-200 hairline, 16 × 24px padding (12 / 24 / 16 in a padding="none" card, pagination footer; add data-padding="none" on the footer itself to zero it); on a subtle surface.

                                Props

                                propvalues (default outlined)meaning
                                variantdefault outlined elevated interactive ghostdefault = a flat surface with a fine outline; outlined = ring only, transparent background (on white canvases); elevated = shadow-md (only on a same-color canvas); interactive = whole card is one link or action: the edge strengthens and the surface changes on hover; ghost = no ring, gray-50 fill (a quiet group inside another card).
                                paddingnone sm md lgInner padding of header/body/footer: none for tables, lists and media that bleed to the edge; sm 16px (compact cards in a grid); md 24px (default); lg 32px (hero cards and settings sections).

                                States

                                stateselectormeaning
                                hover:hoverOnly interactive cards react: a stronger edge and subtle fill. (native)
                                focus:focus-visibleKeyboard focus. Shows the 3px ring; never on mouse click. (native :focus-visible)
                                selected[aria-selected="true"], &[data-selected]Selected item. (aria-selected="true" or data-selected)

                                Rules

                                • Resting cards have a fine outline. Only floating layers use elevation; interactive cards respond through their edge and surface.
                                • One title per card. Use a CardHeader (padding none) when the title needs a hairline, a badge, media or tabs; the plain header part is for simple titled cards.
                                • Do not nest default cards inside default cards. Inside a card use ghost cards, dividers or plain groups.
                                • A collection of similar items is a list or a table inside one card, not a grid of cards. Cards for a grid only when each item is visual (media) or the number is ≤ 6.
                                • Padding is a prop, never ad-hoc: md 24px by default, sm 16px in dense grids, lg 32px for hero and settings cards, none when a table, list or media fills the card.
                                • Interactive cards contain no other interactive elements (no buttons inside a link) and show a stronger edge on hover; selected cards (radio cards) carry data-selected and use the selected outline and surface tokens.
                                • Footers hold meta or the pagination / actions row, on a hairline, on a subtle surface.

                                Accessibility

                                • Interactive cards are an <a> (navigation) or a <button> (action), never a div with onClick; selectable cards are radios or checkboxes with the card as their label.
                                • Use <section> with an aria-labelledby pointing to the title when the card is a landmark of the page.
                                • The focus ring follows the active token on :focus-visible; the selected ring is separate and does not replace it.
                                Layout

                                Divider .cn-divideredit

                                A 1px gray-200 hairline that separates. Horizontal between blocks, vertical between inline controls, or the reference content divider carrying text, a heading, a button or a button group on the line, between two lines, or on a gray-50 band.

                                Use: Between sections of a card or form, between groups in a toolbar or menu, to label a break ("Earlier", "Or continue with") or to place an action between two blocks ("Add step"). Not between every list row or table row (those components draw their own rules), and never as decoration.

                                Horizontal (default)Component previewA plain <hr>: 1px gray-200 with 16px above and below.

                                Lumen keeps every project, task and file in one workspace your team can search.


                                Invite teammates, set roles and connect the tools you already use.

                                HTML
                                <div style="width:100%;max-width:480px"><p class="cn-text-body-md" style="color:var(--cn-color-fg-muted);margin:0">Lumen keeps every project, task and file in one workspace your team can search.</p><hr class="cn-divider" data-orientation="horizontal" data-variant="single" data-spacing="md"><p class="cn-text-body-md" style="color:var(--cn-color-fg-muted);margin:0">Invite teammates, set roles and connect the tools you already use.</p></div>
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-divider <hr> — An <hr> for a plain horizontal rule. A <div role="separator"> when vertical or when it carries content (add data-label="center|start|end").
                                • .cn-divider__content <span> · optional — Optional slot on the line: text, a heading, a Button or a ButtonGroup. Centered, or at the start / end with data-label.
                                • .cn-divider__label <span> · optional — Plain text inside the content slot: 14px medium fg-muted ("Earlier", "Or"). A heading uses a real h-tag with the heading-xs class instead.

                                Props

                                propvalues (default outlined)meaning
                                orientationhorizontal verticalhorizontal = full-width rule between stacked blocks; vertical = 1px column that stretches to the height of a flex row (toolbars, inline meta).
                                variantsingle dual fillsingle = one hairline, with the content sitting on it (the default). dual = a hairline above and below the content row (44px with text, 56 with buttons), for section breaks that carry an action. fill = a gray-50 band (36 / 52px) holding the content, for sticky sub-headers and grouped feeds.
                                spacingnone sm md lgMargin around the rule: none 0 (inside components that manage their own spacing), sm 8px (toolbars, dense cards), md 16px (default inside cards and forms), lg 32px (between page sections). Applies as margin-block when horizontal and margin-inline when vertical.

                                Rules

                                • A divider separates two things that are both present; never end a block with one and never stack two.
                                • Do not add dividers between rows of List, Table, DescriptionList or Accordion; they draw their own rules.
                                • Spacing is a prop, never an ad-hoc margin: sm in toolbars and dense cards, md inside cards and forms, lg between page sections, none inside components that already space their children.
                                • Prefer whitespace over a divider when the two blocks already differ in type (a heading followed by a paragraph needs no rule).
                                • Text on the line is 14px medium muted, 1–3 words ("Earlier", "Or", "Today"); a heading on the line is a real h-tag in heading-xs; a labeled divider is not a section title on its own.
                                • One action on a divider at most: an outline or ghost sm Button, or one ButtonGroup; never a primary button.
                                • single by default; dual only when the divider carries an action between two long blocks; fill for sticky date bands in feeds.
                                • Vertical dividers only between inline controls or meta of the same height; never taller than the row.
                                • Always gray-200; never a stronger color, never thicker than 1px, never dashed (dashed is reserved for drop zones).

                                Accessibility

                                • A plain horizontal rule is an <hr> (implicit role separator). A labeled or vertical one is a <div role="separator">; add aria-orientation="vertical" when vertical.
                                • The label is read as text; keep it meaningful on its own ("Earlier" is fine, "———" is not).
                                • A button or button group on the divider is focusable in the normal order; the separator role does not hide it.
                                • Decorative dividers inside a component that already conveys grouping may use aria-hidden="true" to reduce noise.
                                Layout

                                CardHeader .cn-card-headeredit

                                The header row of a Card (the reference table-card header): 20 × 24px padding, a 16px semibold title with an optional gray badge, a 14px gray-600 description, an optional 40px avatar or modern featured icon before the text and the card's actions on the right, closed by a gray-200 hairline that spans the card.

                                Use: Use as the first child of a Card that holds a table, a list, a form section or a record. It names the card and carries its actions (Import, Add user, the "more" menu). For a page title use PageHeader; for a heading between two blocks of a page use SectionHeader. Put it in a Card with data-padding="none" so the hairline reaches the card edges.

                                Default (table card)Component previewCard with data-padding="none" so the header hairline spans the full width; 20 × 24px padding, 16px semibold title.
                                1280 × 384

                                Team members

                                Manage your team members and their account permissions here.

                                24 members · 3 pending invites · Last change 2 hours ago by Daniel Costa.

                                HTML
                                <section class="cn-card" data-variant="default" data-padding="none" style="width:100%;max-width:720px"><header class="cn-card-header" data-variant="default" data-size="md"><div class="cn-card-header__content"><h2 class="cn-card-header__title">Team members</h2><p class="cn-card-header__description">Manage your team members and their account permissions here.</p></div><div class="cn-card-header__actions"><button type="button" class="cn-button" data-variant="outline" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-button__label">Import</span></button><button type="button" class="cn-button" data-variant="primary" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg><span class="cn-button__label">Add user</span></button></div></header><div class="cn-card__body" style="padding:var(--cn-space-5) var(--cn-space-6)"><p class="cn-text-body-md">24 members · 3 pending invites · Last change 2 hours ago by Daniel Costa.</p></div></section>
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-card-header <header> — Wrapping flex row inside the Card: media, text block, actions; 16px gaps, aligned to the top, hairline below. Padding from size.
                                • .cn-card-header__media <div> · optional — Optional 40px block before the text: a modern gray FeaturedIcon (md) or an Avatar md.
                                • .cn-card-header__content <div> — Column with 2px gap: title (with optional badge) then description. Grows to push actions right.
                                • .cn-card-header__title <h2> — 16px semibold in ink (heading-xs); a flex row so a Badge can follow the text 8px after it.
                                • .cn-card-header__badge <span> · optional — A cn-badge (modern, gray, sm, square) inside the title after the text (add this class to the badge): "100 users", "Beta".
                                • .cn-card-header__description <p> · optional — 14px supporting text in fg-muted.
                                • .cn-card-header__actions <div> · optional — Row of Buttons or ghost IconButtons 12px apart; the "more" menu trigger is last.
                                • .cn-card-header__tabs <div> · optional — Full-width slot under the text for an underline Tabs sm whose bar sits on the header hairline.

                                Props

                                propvalues (default outlined)meaning
                                variantdefault with-badge with-media with-tabsdefault = title, description, actions. with-badge = a Badge after the title (count or state of the card). with-media = a 40px avatar or featured icon before the text (a person, a company, a feature). with-tabs = underline Tabs under the text switch views of the card's content.
                                sizesm mdmd = 20px vertical, 24px horizontal padding (table cards, settings cards). sm = 16px × 20px for compact cards in a grid or a side column.

                                Rules

                                • Use inside a Card with data-padding="none" and give the body its own padding, so the header hairline reaches the card edges.
                                • Title is a noun phrase in sentence case, 1–3 words, 16px semibold; the description is one sentence in 14px muted. Never repeat the page title in a card title.
                                • Actions: one primary Button at most, outline for the rest, ghost IconButtons for copy / more; the "more" menu is always last, 12px apart.
                                • A Badge after the title is a modern gray sm badge with a count or a state of the card content, never decoration; one per title.
                                • with-media: an Avatar md for a person or company, a modern gray FeaturedIcon md for a feature or setting. Never both.
                                • with-tabs: Tabs are underline, sm, and switch views of the card's own content; their bar sits on the header hairline (the tabs lose their own line).
                                • md padding (20 × 24) on table and settings cards; sm (16 × 20) on cards narrower than 400px. Under 768px the horizontal padding drops to 16.
                                • Keep the header on one row when space allows. Let actions wrap below the content before the title or description becomes a narrow column.

                                Accessibility

                                • Root is a <header> inside the Card <section>; the title heading level follows the page outline (h2 under a PageHeader h1).
                                • A Badge inside the title becomes part of the heading name; keep it short and meaningful.
                                • IconButtons in actions need aria-label; the "more" trigger exposes aria-haspopup="menu".
                                • Tabs under the header are route links with aria-current="page" or a role="tablist" with aria-selected.
                                • The featured icon is decorative (aria-hidden); an Avatar whose name is the title is aria-hidden too, so the name is not read twice.
                                Layout

                                ContentDivider .cn-content-divideredit

                                A hairline that carries content: body-sm text centered or at the start, an outline sm Button, a 24px icon, or a pill chip in the middle of the line. The richer sibling of Divider for long content, threads, auth forms and load-more rows.

                                Use: Use where a plain rule is not enough: "Or continue with" on sign-in, "Earlier this week" in a feed, "Show 12 more comments" in a thread, "3 new messages" in a chat. For a plain rule or a kicker-voice label use Divider; for a section heading use SectionHeader.

                                PlainComponent preview

                                Maya approved the launch plan and asked for a final read of the pricing page.

                                Daniel uploaded the updated timeline.

                                HTML
                                <div style="width:100%;max-width:560px"><p class="cn-text-body-sm" style="color:var(--cn-color-fg-muted);margin:0">Maya approved the launch plan and asked for a final read of the pricing page.</p><div class="cn-content-divider" data-variant="plain" data-spacing="md" role="separator"><span class="cn-content-divider__line" aria-hidden="true"></span></div><p class="cn-text-body-sm" style="color:var(--cn-color-fg-muted);margin:0">Daniel uploaded the updated timeline.</p></div>
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-content-divider <div> — Flex row, centered, 16px gaps; role="separator" when it only carries text, no role when it holds a button.
                                • .cn-content-divider__line <span> — The 1px border-default rule that fills the free space (flex 1). aria-hidden. One on each side, or one after the label in text-left.
                                • .cn-content-divider__label <span> · optional — body-sm muted text on the line ("Or continue with", "Earlier this week").
                                • .cn-content-divider__button <div> · optional — Wrapper for an outline sm Button in the middle ("Show 12 more comments").
                                • .cn-content-divider__icon <span> · optional — 24px icon in fg-subtle on the line, for a purely visual break.
                                • .cn-content-divider__pill <span> · optional — label-sm chip in bg-surface with a hairline, radius full, 28px tall ("3 new messages").

                                Props

                                propvalues (default outlined)meaning
                                variantplain text text-left button icon pillplain = the line only (same as Divider, kept here for consistency inside content that mixes variants). text = centered body-sm label between two lines. text-left = the label at the start followed by one line (group headings in feeds). button = an outline sm Button between two lines (load more, show earlier). icon = a 24px icon between two lines (a decorative break in an article). pill = a chip between two lines (unread markers, "new since your last visit").
                                spacingsm md lgVertical margin: sm 16px inside cards and threads, md 24px between blocks of content, lg 40px between sections of an article.

                                Rules

                                • A content divider separates two things that are both present and adds one piece of information about the break; if there is nothing to say, use Divider.
                                • Text labels are 2–4 words in sentence case, body-sm muted: a time ("Earlier this week"), an alternative ("Or continue with"), a count ("3 new messages"). Never a heading.
                                • One element in the middle at most: text, a button, an icon or a pill. Never a button and text together.
                                • The button variant is for revealing more of the same content ("Show 12 more comments", "Load earlier messages"); it is an outline sm Button and the count is in its label.
                                • Pills mark a boundary the user must notice (unread, new since last visit) and disappear once the content below is seen.
                                • Icons are decorative breaks in long-form reading; one per article at most, always aria-hidden.
                                • Spacing is a prop: sm in cards and threads, md between blocks, lg between article sections. Never add margins around it.
                                • Do not put content dividers between List, Table or Timeline rows; those components draw their own rules.

                                Accessibility

                                • When the divider only carries text or an icon, the root is role="separator" and the lines are aria-hidden; the label is read as the separator's name.
                                • When it carries a Button, the root has no role: the button is a normal control in the tab order and the lines stay aria-hidden.
                                • Labels must be meaningful on their own ("Earlier this week" is fine; a lone dash is not).
                                • A pill that marks unread content also updates an aria-live region or the page title so the count is announced.
                                Layout

                                PageHeader .cn-page-headeredit

                                Top of an application page: title, supporting text and the actions for the page, optionally with breadcrumbs above, a search field, an avatar, or tabs below. The "page header" pattern.

                                Use: Exactly one per page, directly under the top navigation. Put the page-level primary action here (at most one filled button). Section titles inside the page use SectionHeader.

                                SimpleComponent preview
                                1280 × 384

                                Team members

                                Manage your team members and their account permissions here.

                                HTML
                                <header class="cn-page-header" data-variant="simple" data-divider="yes"><div class="cn-page-header__content"><h1 class="cn-page-header__title">Team members</h1><p class="cn-page-header__description">Manage your team members and their account permissions here.</p></div><div class="cn-page-header__actions"><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Secondary</span></button><button type="button" class="cn-button" data-variant="primary" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg><span class="cn-button__label">Add member</span></button></div></header>
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-page-header <header> — Flex row: content left, actions right; wraps on mobile.
                                • .cn-page-header__crumbs <nav> · optional — Optional breadcrumb row above the title (uses Breadcrumb markup).
                                • .cn-page-header__media <span> · optional — Optional 56px avatar or featured icon before the content.
                                • .cn-page-header__content <div> — Title + description column.
                                • .cn-page-header__title <h1> — heading-lg (30/38 semibold). One h1 per page.
                                • .cn-page-header__description <p> — body-lg (16/24) in fg-muted. One sentence.
                                • .cn-page-header__actions <div> — Right-aligned row of buttons; on mobile it stacks under the content.
                                • .cn-page-header__search <div> · optional — Optional search Input in the actions row (width 320).
                                • .cn-page-header__tabs <div> · optional — Optional Tabs (underline) below, bleeding to the header edges.

                                Props

                                propvalues (default outlined)meaning
                                variantsimple with-search with-tabs with-avatar centeredsimple = title/description/actions; with-search = a search field before the actions; with-tabs = tabs under the header; with-avatar = 56px media block before the title (profile pages); centered = title and description centered, no actions (informational pages).
                                divideryes noyes = 1px hairline under the header (or under the tabs).

                                Rules

                                • The title is the h1 and matches the navigation item that brought the user here.
                                • Description is optional and never longer than one sentence.
                                • At most one primary button; secondary actions are outline; overflow goes into a ⋯ IconButton with a Menu.
                                • 20px below the header (the padding) then 32px to the first section when there is a divider; with tabs, the tab list sits on the divider line.
                                • On mobile (< 768) actions move under the content and stretch full width.

                                Accessibility

                                • One h1 per page; the breadcrumb nav has aria-label="Breadcrumb".
                                • Search fields have an aria-label when the placeholder is the only label.
                                Layout

                                SectionHeader .cn-section-headeredit

                                Heading row of a section inside a page or a card: title, supporting text, optional badge, tabs or search, and section-level actions. Smaller than PageHeader.

                                Use: Above tables, forms, lists and settings groups. Use inside Card as its header row when the card holds a data set. Do not use for the page title (PageHeader).

                                DefaultComponent preview
                                1280 × 384

                                Personal info

                                Update your photo and personal details here.

                                HTML
                                <div class="cn-section-header" data-variant="default" data-divider="yes"><div class="cn-section-header__content"><h2 class="cn-section-header__title">Personal info</h2><p class="cn-section-header__description">Update your photo and personal details here.</p></div><div class="cn-section-header__actions"><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Cancel</span></button><button type="button" class="cn-button" data-variant="primary" data-size="md"><span class="cn-button__label">Save</span></button></div></div>
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-section-header <div> — Flex row with a bottom hairline (divider prop).
                                • .cn-section-header__content <div> — Title + description column.
                                • .cn-section-header__title <h2> — heading-sm (18/28 semibold) with optional inline badge.
                                • .cn-section-header__badge <span> · optional — Optional count/status badge after the title (Badge markup).
                                • .cn-section-header__description <p> — body-md in fg-muted.
                                • .cn-section-header__actions <div> — Right-aligned buttons (sm or md).
                                • .cn-section-header__tabs <div> · optional — Optional Tabs (pill/button variant) in the actions slot or below.
                                • .cn-section-header__search <div> · optional — Optional search Input (width 280) in the actions slot.

                                Props

                                propvalues (default outlined)meaning
                                variantdefault with-badge with-tabs with-search carddefault; with-badge = count/status next to the title; with-tabs = filter tabs on the right; with-search = search input on the right; card = padding for use as a Card header (20/24px) with the divider.
                                divideryes noHairline under the header.

                                Rules

                                • Title is an h2 (or h3 inside a card that already has an h2). Never a page title.
                                • Description one sentence; omit rather than repeat the title.
                                • Actions on the right are sm inside cards, md on the page. At most one primary.
                                • 20px padding below, hairline divider, then 24px (space.6) to the content.

                                Accessibility

                                • Headings keep document order: h1 (page) → h2 (section) → h3 (card inside a section).
                                • Filter tabs use role="tablist" only when they switch panels; otherwise use a SegmentedControl.
                                Media

                                MediaFrame .cn-media-frameedit

                                A figure that holds an image or video at a fixed aspect ratio: a clipping frame with a subtle fill, an optional hairline, an optional flat text overlay, and a caption below. The frame clips; the figure does not, so captions never get cut.

                                Use: Use for every image or video in the product: company logos in cards, screenshots in help articles, thumbnails in lists, a video in an empty state. Never place a raw <img> in a layout; the frame guarantees the ratio, the loading placeholder and the radius.

                                Default (16:9, radius xl, cover)
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                HTML
                                <figure class="cn-media-frame" data-ratio="16:9" data-radius="xl" data-fit="cover" style="width:320px"><div class="cn-media-frame__frame"><svg class="cn-media-frame__media" viewBox="0 0 160 90" preserveAspectRatio="xMidYMid slice" role="img" aria-label="Placeholder image"><rect width="160" height="90" fill="var(--cn-color-bg-muted)"/><path d="M0 90L160 0" stroke="var(--cn-color-border-strong)" stroke-width="1"/></svg></div><figcaption class="cn-media-frame__caption">Osaka port, container terminal · Photo: Nakamura Trading Co.</figcaption></figure>
                                Square thumbnail, bordered, radius mdcontain keeps logos whole on the subtle fill; the hairline separates light logos from a white card.
                                HTML
                                <figure class="cn-media-frame" data-ratio="1:1" data-radius="md" data-fit="contain" data-bordered style="width:96px"><div class="cn-media-frame__frame"><svg class="cn-media-frame__media" viewBox="0 0 160 90" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Placeholder image"><rect width="160" height="90" fill="var(--cn-color-bg-muted)"/><path d="M0 90L160 0" stroke="var(--cn-color-border-strong)" stroke-width="1"/></svg></div></figure>
                                4:3 with overlayThe overlay is a flat scrim with inverse text; no gradient.
                                How agents verify a company · 3:40
                                HTML
                                <figure class="cn-media-frame" data-ratio="4:3" data-radius="xl" data-fit="cover" style="width:280px"><div class="cn-media-frame__frame"><svg class="cn-media-frame__media" viewBox="0 0 160 90" preserveAspectRatio="xMidYMid slice" role="img" aria-label="Placeholder image"><rect width="160" height="90" fill="var(--cn-color-bg-muted)"/><path d="M0 90L160 0" stroke="var(--cn-color-border-strong)" stroke-width="1"/></svg><div class="cn-media-frame__overlay">How agents verify a company · 3:40</div></div></figure>
                                Loading
                                Loading preview…
                                HTML
                                <figure class="cn-media-frame" data-ratio="3:2" data-radius="xl" data-fit="cover" data-state="loading" style="width:240px" aria-busy="true"><div class="cn-media-frame__frame"><svg class="cn-media-frame__media" viewBox="0 0 160 90" preserveAspectRatio="xMidYMid slice" role="img" aria-label="Placeholder image"><rect width="160" height="90" fill="var(--cn-color-bg-muted)"/><path d="M0 90L160 0" stroke="var(--cn-color-border-strong)" stroke-width="1"/></svg></div><figcaption class="cn-media-frame__caption">Loading preview…</figcaption></figure>
                                Auto ratio, no radius (full bleed)auto takes the media’s own height. Use inside a Card with padding none.
                                HTML
                                <figure class="cn-media-frame" data-ratio="auto" data-radius="none" data-fit="cover" style="width:320px"><div class="cn-media-frame__frame"><svg class="cn-media-frame__media" viewBox="0 0 160 40" role="img" aria-label="Placeholder banner"><rect width="160" height="40" fill="var(--cn-color-bg-muted)"/><path d="M0 40L160 0" stroke="var(--cn-color-border-strong)" stroke-width="1"/></svg></div></figure>

                                Matrix

                                ratio ↓ / radius →nonemdxl2xl
                                auto
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                1:1
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                4:3
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                3:2
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                16:9
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                Osaka port, container terminal · Photo: Nakamura Trading Co.
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-media-frame <figure> — The figure. Sets width; overflow visible so the caption sits below. data-ratio / data-radius / data-fit, optional data-bordered, data-state="loading".
                                • .cn-media-frame__frame <div> — The clipping box: aspect-ratio, radius, subtle fill, overflow hidden. Holds media and overlay.
                                • .cn-media-frame__media <img> — The <img>, <video> or inline <svg>. Fills the frame; object-fit from data-fit. Always has alt (or aria-label for svg).
                                • .cn-media-frame__overlay <div> · optional — Optional flat scrim over the media with inverse text at the bottom-left: a title, a duration, a label. No gradient.
                                • .cn-media-frame__caption <figcaption> · optional — body-xs muted line under the frame: source, date, what the image shows.

                                Props

                                propvalues (default outlined)meaning
                                ratioauto 1:1 4:3 3:2 16:9Aspect ratio of the frame. auto = the media’s own height (logos, inline illustrations); 1:1 = avatars, product shots; 4:3 = documents and screenshots; 3:2 = photos; 16:9 = video and hero thumbnails (default).
                                radiusnone md xl 2xlCorner radius of the frame. none = full-bleed inside a card with padding none; md = small thumbnails in rows; xl = default for cards and articles; 2xl = hero media on marketing pages.
                                fitcover containcover = fills the frame and crops (photos, screenshots); contain = shows the whole media on the subtle fill (logos, diagrams).

                                States

                                stateselectormeaning
                                loading[data-state="loading"]Media not yet available: the frame shows the skeleton shimmer and the media is hidden. (data-state="loading" on the figure)
                                bordered[data-bordered]Adds the default hairline around the frame, for light media on white surfaces (logos, screenshots). (data-bordered attribute on the figure)

                                Rules

                                • Every image and video in the product lives in a MediaFrame; a raw <img> in a layout is a lint error.
                                • Pick the ratio from the content, not the space: 1:1 logos and avatars, 4:3 screenshots, 3:2 photos, 16:9 video. Do not stretch a screenshot into 16:9.
                                • Width comes from the layout (100% of its column); set it inline only in isolated thumbnails.
                                • Logos and diagrams use fit="contain" and data-bordered; photos use cover without a border.
                                • The overlay is flat (bg-overlay) with one line of label-md inverse text at the bottom-left. No gradients, no blur, no second line.
                                • Captions are body-xs muted, one line, below the frame: source, date, or what the picture shows. They are not titles.
                                • Radius matches the container: md in rows, xl in cards, 2xl on marketing heroes, none when the media bleeds to the card edge.
                                • Show data-state="loading" only for network images; inline SVGs render immediately.

                                Accessibility

                                • Every <img> has alt text that says what it shows; decorative images use alt="" and the figure gets no caption.
                                • Inline SVG media uses role="img" with aria-label; videos have captions or a transcript.
                                • The figcaption is the accessible description; do not repeat the alt text in it.
                                • Overlay text keeps AA contrast on the scrim (inverse text on bg-overlay); never place text over unscrimmed media.
                                Media

                                CreditCard .cn-credit-cardedit

                                A decorative payment card at the real 1.586 ratio: logo and chip on top, a masked number in the middle, holder and expiry at the bottom next to a generic network mark. Four finishes (brand, dark, light, outline) and two sizes.

                                Use: Use in billing settings, checkout summaries and the payment-method picker to show which saved card is in use. It is an illustration of a record, not a form: editing happens in a Dialog with real Inputs. Never render a full card number.

                                Brand (default)The product card: action fill, on-action text, a quiet circle in the corner.
                                HTML
                                <div class="cn-credit-card" data-variant="brand" data-size="md" role="img" aria-label="Lumen card ending in 4242, Maya Chen, expires 09/29"><div class="cn-credit-card__header"><span class="cn-credit-card__logo"><svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path d="M10 1.5l2.4 6.1 6.1 2.4-6.1 2.4L10 18.5l-2.4-6.1L1.5 10l6.1-2.4z"/></svg>Lumen</span><svg class="cn-credit-card__chip" viewBox="0 0 32 24" fill="none" stroke="currentColor" stroke-width="1.25" stroke-linejoin="round" aria-hidden="true"><rect x="1" y="1" width="30" height="22" rx="4" style="fill:currentColor;fill-opacity:.18"/><path d="M1 9h9a3 3 0 013 3v0a3 3 0 01-3 3H1M31 9h-9a3 3 0 00-3 3v0a3 3 0 003 3h9M13 1v8M19 1v8M13 15v8M19 15v8"/></svg></div><div class="cn-credit-card__number">•••• •••• •••• 4242</div><div class="cn-credit-card__footer"><div class="cn-credit-card__holder"><span><span class="cn-credit-card__caption">Card holder</span><span class="cn-credit-card__name">Maya Chen</span></span><span><span class="cn-credit-card__caption">Expires</span><span class="cn-credit-card__expiry">09/29</span></span></div><svg class="cn-credit-card__brand" viewBox="0 0 40 24" aria-hidden="true"><circle cx="14" cy="12" r="11" style="fill:currentColor;fill-opacity:.55"/><circle cx="26" cy="12" r="11" style="fill:currentColor;fill-opacity:.35"/></svg></div></div>
                                DarkInverse surface; flips to near-white in the dark theme.
                                HTML
                                <div class="cn-credit-card" data-variant="dark" data-size="md" role="img" aria-label="Lumen card ending in 8210, Daniel Costa, expires 03/28"><div class="cn-credit-card__header"><span class="cn-credit-card__logo"><svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path d="M10 1.5l2.4 6.1 6.1 2.4-6.1 2.4L10 18.5l-2.4-6.1L1.5 10l6.1-2.4z"/></svg>Lumen</span><svg class="cn-credit-card__chip" viewBox="0 0 32 24" fill="none" stroke="currentColor" stroke-width="1.25" stroke-linejoin="round" aria-hidden="true"><rect x="1" y="1" width="30" height="22" rx="4" style="fill:currentColor;fill-opacity:.18"/><path d="M1 9h9a3 3 0 013 3v0a3 3 0 01-3 3H1M31 9h-9a3 3 0 00-3 3v0a3 3 0 003 3h9M13 1v8M19 1v8M13 15v8M19 15v8"/></svg></div><div class="cn-credit-card__number">•••• •••• •••• 8210</div><div class="cn-credit-card__footer"><div class="cn-credit-card__holder"><span><span class="cn-credit-card__caption">Card holder</span><span class="cn-credit-card__name">Daniel Costa</span></span><span><span class="cn-credit-card__caption">Expires</span><span class="cn-credit-card__expiry">03/28</span></span></div><svg class="cn-credit-card__brand" viewBox="0 0 40 24" aria-hidden="true"><circle cx="14" cy="12" r="11" style="fill:currentColor;fill-opacity:.55"/><circle cx="26" cy="12" r="11" style="fill:currentColor;fill-opacity:.35"/></svg></div></div>
                                LightSurface with a hairline, for lists of several saved cards.
                                HTML
                                <div class="cn-credit-card" data-variant="light" data-size="md" role="img" aria-label="Lumen card ending in 1187, Sofia Almeida, expires 11/27"><div class="cn-credit-card__header"><span class="cn-credit-card__logo"><svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path d="M10 1.5l2.4 6.1 6.1 2.4-6.1 2.4L10 18.5l-2.4-6.1L1.5 10l6.1-2.4z"/></svg>Lumen</span><svg class="cn-credit-card__chip" viewBox="0 0 32 24" fill="none" stroke="currentColor" stroke-width="1.25" stroke-linejoin="round" aria-hidden="true"><rect x="1" y="1" width="30" height="22" rx="4" style="fill:currentColor;fill-opacity:.18"/><path d="M1 9h9a3 3 0 013 3v0a3 3 0 01-3 3H1M31 9h-9a3 3 0 00-3 3v0a3 3 0 003 3h9M13 1v8M19 1v8M13 15v8M19 15v8"/></svg></div><div class="cn-credit-card__number">•••• •••• •••• 1187</div><div class="cn-credit-card__footer"><div class="cn-credit-card__holder"><span><span class="cn-credit-card__caption">Card holder</span><span class="cn-credit-card__name">Sofia Almeida</span></span><span><span class="cn-credit-card__caption">Expires</span><span class="cn-credit-card__expiry">11/27</span></span></div><svg class="cn-credit-card__brand" viewBox="0 0 40 24" aria-hidden="true"><circle cx="14" cy="12" r="11" style="fill:currentColor;fill-opacity:.55"/><circle cx="26" cy="12" r="11" style="fill:currentColor;fill-opacity:.35"/></svg></div></div>
                                OutlineTransparent placeholder look for "add a card" states and disabled methods.
                                HTML
                                <div class="cn-credit-card" data-variant="outline" data-size="md" role="img" aria-label="Lumen card ending in 0093, Aisha Khan, expires 06/30"><div class="cn-credit-card__header"><span class="cn-credit-card__logo"><svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path d="M10 1.5l2.4 6.1 6.1 2.4-6.1 2.4L10 18.5l-2.4-6.1L1.5 10l6.1-2.4z"/></svg>Lumen</span><svg class="cn-credit-card__chip" viewBox="0 0 32 24" fill="none" stroke="currentColor" stroke-width="1.25" stroke-linejoin="round" aria-hidden="true"><rect x="1" y="1" width="30" height="22" rx="4" style="fill:currentColor;fill-opacity:.18"/><path d="M1 9h9a3 3 0 013 3v0a3 3 0 01-3 3H1M31 9h-9a3 3 0 00-3 3v0a3 3 0 003 3h9M13 1v8M19 1v8M13 15v8M19 15v8"/></svg></div><div class="cn-credit-card__number">•••• •••• •••• 0093</div><div class="cn-credit-card__footer"><div class="cn-credit-card__holder"><span><span class="cn-credit-card__caption">Card holder</span><span class="cn-credit-card__name">Aisha Khan</span></span><span><span class="cn-credit-card__caption">Expires</span><span class="cn-credit-card__expiry">06/30</span></span></div><svg class="cn-credit-card__brand" viewBox="0 0 40 24" aria-hidden="true"><circle cx="14" cy="12" r="11" style="fill:currentColor;fill-opacity:.55"/><circle cx="26" cy="12" r="11" style="fill:currentColor;fill-opacity:.35"/></svg></div></div>
                                Small, selected in a picker256px cards for pickers and mobile; data-selected adds the action-colored ring.
                                HTML
                                <div style="display:flex;flex-wrap:wrap;gap:var(--cn-space-4);align-items:flex-start"><div class="cn-credit-card" data-variant="brand" data-size="sm" role="img" data-selected aria-label="Lumen card ending in 4242, Maya Chen, expires 09/29"><div class="cn-credit-card__header"><span class="cn-credit-card__logo"><svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path d="M10 1.5l2.4 6.1 6.1 2.4-6.1 2.4L10 18.5l-2.4-6.1L1.5 10l6.1-2.4z"/></svg>Lumen</span><svg class="cn-credit-card__chip" viewBox="0 0 32 24" fill="none" stroke="currentColor" stroke-width="1.25" stroke-linejoin="round" aria-hidden="true"><rect x="1" y="1" width="30" height="22" rx="4" style="fill:currentColor;fill-opacity:.18"/><path d="M1 9h9a3 3 0 013 3v0a3 3 0 01-3 3H1M31 9h-9a3 3 0 00-3 3v0a3 3 0 003 3h9M13 1v8M19 1v8M13 15v8M19 15v8"/></svg></div><div class="cn-credit-card__number">•••• •••• •••• 4242</div><div class="cn-credit-card__footer"><div class="cn-credit-card__holder"><span><span class="cn-credit-card__caption">Card holder</span><span class="cn-credit-card__name">Maya Chen</span></span><span><span class="cn-credit-card__caption">Expires</span><span class="cn-credit-card__expiry">09/29</span></span></div><svg class="cn-credit-card__brand" viewBox="0 0 40 24" aria-hidden="true"><circle cx="14" cy="12" r="11" style="fill:currentColor;fill-opacity:.55"/><circle cx="26" cy="12" r="11" style="fill:currentColor;fill-opacity:.35"/></svg></div></div><div class="cn-credit-card" data-variant="light" data-size="sm" role="img" aria-label="Lumen card ending in 1187, Maya Chen, expires 11/27"><div class="cn-credit-card__header"><span class="cn-credit-card__logo"><svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path d="M10 1.5l2.4 6.1 6.1 2.4-6.1 2.4L10 18.5l-2.4-6.1L1.5 10l6.1-2.4z"/></svg>Lumen</span><svg class="cn-credit-card__chip" viewBox="0 0 32 24" fill="none" stroke="currentColor" stroke-width="1.25" stroke-linejoin="round" aria-hidden="true"><rect x="1" y="1" width="30" height="22" rx="4" style="fill:currentColor;fill-opacity:.18"/><path d="M1 9h9a3 3 0 013 3v0a3 3 0 01-3 3H1M31 9h-9a3 3 0 00-3 3v0a3 3 0 003 3h9M13 1v8M19 1v8M13 15v8M19 15v8"/></svg></div><div class="cn-credit-card__number">•••• •••• •••• 1187</div><div class="cn-credit-card__footer"><div class="cn-credit-card__holder"><span><span class="cn-credit-card__caption">Card holder</span><span class="cn-credit-card__name">Maya Chen</span></span><span><span class="cn-credit-card__caption">Expires</span><span class="cn-credit-card__expiry">11/27</span></span></div><svg class="cn-credit-card__brand" viewBox="0 0 40 24" aria-hidden="true"><circle cx="14" cy="12" r="11" style="fill:currentColor;fill-opacity:.55"/><circle cx="26" cy="12" r="11" style="fill:currentColor;fill-opacity:.35"/></svg></div></div></div>

                                Matrix

                                variant ↓ / size →smmd
                                brand
                                dark
                                light
                                outline
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-credit-card <div> — The card: 316px wide (256 in sm), aspect-ratio 1.586, radius 2xl, 24px padding, flex column with the number centered vertically. role="img" with an aria-label that names the card.
                                • .cn-credit-card__header <div> — Top row: logo on the left, chip on the right.
                                • .cn-credit-card__logo <span> — Issuer or product wordmark, label-md semibold with a 20px mark.
                                • .cn-credit-card__chip <svg> — The 32px contact chip drawn in currentColor at reduced opacity.
                                • .cn-credit-card__number <div> — The masked number "•••• •••• •••• 4242" in numeric-md, tabular figures and wider tracking.
                                • .cn-credit-card__footer <div> — Bottom row: holder block on the left, network mark on the right.
                                • .cn-credit-card__holder <div> — Name and expiry side by side, each under a tiny caption.
                                • .cn-credit-card__caption <span> — Kicker-voice caption above name and expiry ("Card holder", "Expires").
                                • .cn-credit-card__name <span> — Cardholder name in label-sm, wide tracking, truncated.
                                • .cn-credit-card__expiry <span> — MM/YY in numeric-md.
                                • .cn-credit-card__brand <svg> — A generic 40px network mark (two overlapping circles in currentColor). Replace with the real network logo when known.

                                Props

                                propvalues (default outlined)meaning
                                variantbrand dark light outlinebrand = action-colored card with on-action text, the default card of the product. dark = inverse surface (ink in light theme, near-white in dark theme). light = surface with a hairline and shadow-xs, for lists of several cards. outline = transparent with a strong hairline, the "add a card" or placeholder look.
                                sizesm mdmd = 316px wide, 24px padding, 16px number (settings pages, checkout). sm = 256px, 16px padding, 13px number for pickers, drawers and mobile.

                                States

                                stateselectormeaning
                                selected[aria-selected="true"], &[data-selected]The chosen card in a picker: a 2px action-colored ring outside the card. (aria-selected="true" (inside a listbox) or data-selected on the root)

                                Rules

                                • Decorative: the card is a picture of a saved payment method, never a form. Editing and adding cards happen in a Dialog with Inputs.
                                • Always mask the number as "•••• •••• •••• 4242": the last four digits only, never more, never the CVC, never an expiry that is not needed on screen.
                                • One brand card per screen; other saved cards use light or outline so the default method stands out.
                                • Keep the 1.586 ratio and the 316/256 widths; never stretch the card to a column. Center it or align it with the form above.
                                • The network mark is generic until the network is known; then use the real mark at 40px, never a colored badge.
                                • Names are shown as stored, truncated with an ellipsis after 160px; expiry is MM/YY.
                                • No gradients, foils or photos; the only decoration is the 8% circle in the corner.
                                • The card never opens anything on click by itself. In a picker, wrap it in a listbox option and use data-selected for the ring.

                                Accessibility

                                • The root has role="img" and an aria-label that says it all: "Lumen card ending in 4242, Maya Chen, expires 09/29". The inner text is then decorative.
                                • When the card is an option in a picker, the option element carries the name and aria-selected; the card inside stays role="img".
                                • Contrast: on-action text on the action fill and fg-inverse on bg-inverse are AA by token design; the light and outline variants use fg-default.
                                • Do not expose the number as a table of digits; screen readers read the masked string with the last four digits.
                                Media

                                VideoPlayer .cn-video-playeredit

                                A 16:9 inverse frame with a poster, a centered 64px play button on a translucent surface circle, and a bottom control bar on a scrim: a 2px progress line with a scrubber, 32px icon buttons in fg-inverse and a mono time label. Three looks: default, minimal (play only) and mockup (a framed device-like shot for marketing).

                                Use: Use for product videos, onboarding clips and recorded sessions inside the app or on marketing pages. The component styles the chrome; the <video> element and its behavior come from the app. For images use MediaFrame; for a slideshow use Carousel.

                                Default (paused)Poster, centered play button and the full bar. The progress line is 2px and grows on hover.
                                0:42 / 3:18
                                HTML
                                <div class="cn-video-player" data-variant="default" data-size="md" data-state="paused" role="region" aria-label="Video: Introducing Lumen reporting"><svg class="cn-video-player__media" viewBox="0 0 640 360" role="img" aria-label="Poster: the Lumen reporting workspace"><rect width="640" height="360" style="fill:var(--cn-color-bg-inverse)"/><rect x="72" y="56" width="496" height="248" rx="16" style="fill:var(--cn-color-fg-inverse);opacity:.08"/><rect x="96" y="80" width="140" height="10" rx="5" style="fill:var(--cn-color-fg-inverse);opacity:.32"/><rect x="96" y="100" width="88" height="8" rx="4" style="fill:var(--cn-color-fg-inverse);opacity:.18"/><rect x="96" y="132" width="212" height="148" rx="10" style="fill:var(--cn-color-fg-inverse);opacity:.1"/><rect x="332" y="132" width="212" height="64" rx="10" style="fill:var(--cn-color-fg-inverse);opacity:.1"/><rect x="332" y="216" width="212" height="64" rx="10" style="fill:var(--cn-color-fg-inverse);opacity:.1"/><path d="M116 250l40-32 38 18 44-52 42 20 28-36" fill="none" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" style="stroke:var(--cn-color-bg-accent)"/></svg><button type="button" class="cn-video-player__play" aria-label="Play"><svg class="cn-icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M5 3.2v9.6L12.6 8z"/></svg></button><div class="cn-video-player__controls"><div class="cn-video-player__progress" role="slider" aria-label="Seek" aria-valuemin="0" aria-valuemax="100" aria-valuenow="22" tabindex="0"><div class="cn-video-player__progress-fill" style="width:22%"></div><span class="cn-video-player__scrubber" style="left:22%"></span></div><div class="cn-video-player__row"><div class="cn-video-player__group"><button type="button" class="cn-video-player__button" aria-label="Play"><svg class="cn-icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M5 3.2v9.6L12.6 8z"/></svg></button><button type="button" class="cn-video-player__button" aria-label="Mute"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 6.25h2.25L8 3.5v9L4.75 9.75H2.5z"/><path d="M10.5 5.75a3 3 0 010 4.5M12.25 4a5.5 5.5 0 010 8"/></svg></button><span class="cn-video-player__time">0:42 / 3:18</span></div><div class="cn-video-player__group"><button type="button" class="cn-video-player__button" aria-label="Captions" aria-pressed="false"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="9" rx="1.5"/><path d="M5 8.75h2.5M8.75 8.75H11M5 6.25h6"/></svg></button><button type="button" class="cn-video-player__button" aria-label="Settings"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg></button><button type="button" class="cn-video-player__button" aria-label="Full screen"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6.5v-3.5h3.5M13 6.5v-3.5H9.5M3 9.5v3.5h3.5M13 9.5v3.5H9.5"/></svg></button></div></div></div></div>
                                Playing, largedata-state="playing" hides the big play button; the bar shows pause.
                                1:54 / 3:18
                                HTML
                                <div class="cn-video-player" data-variant="default" data-size="lg" data-state="playing" role="region" aria-label="Video: Introducing Lumen reporting"><svg class="cn-video-player__media" viewBox="0 0 640 360" role="img" aria-label="Poster: the Lumen reporting workspace"><rect width="640" height="360" style="fill:var(--cn-color-bg-inverse)"/><rect x="72" y="56" width="496" height="248" rx="16" style="fill:var(--cn-color-fg-inverse);opacity:.08"/><rect x="96" y="80" width="140" height="10" rx="5" style="fill:var(--cn-color-fg-inverse);opacity:.32"/><rect x="96" y="100" width="88" height="8" rx="4" style="fill:var(--cn-color-fg-inverse);opacity:.18"/><rect x="96" y="132" width="212" height="148" rx="10" style="fill:var(--cn-color-fg-inverse);opacity:.1"/><rect x="332" y="132" width="212" height="64" rx="10" style="fill:var(--cn-color-fg-inverse);opacity:.1"/><rect x="332" y="216" width="212" height="64" rx="10" style="fill:var(--cn-color-fg-inverse);opacity:.1"/><path d="M116 250l40-32 38 18 44-52 42 20 28-36" fill="none" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" style="stroke:var(--cn-color-bg-accent)"/></svg><button type="button" class="cn-video-player__play" aria-label="Play"><svg class="cn-icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M5 3.2v9.6L12.6 8z"/></svg></button><div class="cn-video-player__controls"><div class="cn-video-player__progress" role="slider" aria-label="Seek" aria-valuemin="0" aria-valuemax="100" aria-valuenow="58" tabindex="0"><div class="cn-video-player__progress-fill" style="width:58%"></div><span class="cn-video-player__scrubber" style="left:58%"></span></div><div class="cn-video-player__row"><div class="cn-video-player__group"><button type="button" class="cn-video-player__button" aria-label="Pause"><svg class="cn-icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><rect x="4" y="3.5" width="2.75" height="9" rx=".5"/><rect x="9.25" y="3.5" width="2.75" height="9" rx=".5"/></svg></button><button type="button" class="cn-video-player__button" aria-label="Mute"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 6.25h2.25L8 3.5v9L4.75 9.75H2.5z"/><path d="M10.5 5.75a3 3 0 010 4.5M12.25 4a5.5 5.5 0 010 8"/></svg></button><span class="cn-video-player__time">1:54 / 3:18</span></div><div class="cn-video-player__group"><button type="button" class="cn-video-player__button" aria-label="Captions" aria-pressed="false"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="9" rx="1.5"/><path d="M5 8.75h2.5M8.75 8.75H11M5 6.25h6"/></svg></button><button type="button" class="cn-video-player__button" aria-label="Settings"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg></button><button type="button" class="cn-video-player__button" aria-label="Full screen"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6.5v-3.5h3.5M13 6.5v-3.5H9.5M3 9.5v3.5h3.5M13 9.5v3.5H9.5"/></svg></button></div></div></div></div>
                                Minimal, smallPoster and play button only, for cards and documentation; the bar appears once playing.
                                HTML
                                <div class="cn-video-player" data-variant="minimal" data-size="sm" data-state="paused" role="region" aria-label="Video: Introducing Lumen reporting"><svg class="cn-video-player__media" viewBox="0 0 640 360" role="img" aria-label="Poster: the Lumen reporting workspace"><rect width="640" height="360" style="fill:var(--cn-color-bg-inverse)"/><rect x="72" y="56" width="496" height="248" rx="16" style="fill:var(--cn-color-fg-inverse);opacity:.08"/><rect x="96" y="80" width="140" height="10" rx="5" style="fill:var(--cn-color-fg-inverse);opacity:.32"/><rect x="96" y="100" width="88" height="8" rx="4" style="fill:var(--cn-color-fg-inverse);opacity:.18"/><rect x="96" y="132" width="212" height="148" rx="10" style="fill:var(--cn-color-fg-inverse);opacity:.1"/><rect x="332" y="132" width="212" height="64" rx="10" style="fill:var(--cn-color-fg-inverse);opacity:.1"/><rect x="332" y="216" width="212" height="64" rx="10" style="fill:var(--cn-color-fg-inverse);opacity:.1"/><path d="M116 250l40-32 38 18 44-52 42 20 28-36" fill="none" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" style="stroke:var(--cn-color-bg-accent)"/></svg><button type="button" class="cn-video-player__play" aria-label="Play"><svg class="cn-icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M5 3.2v9.6L12.6 8z"/></svg></button></div>
                                Mockup for a marketing sectionThe same chrome inside a framed, shadowed shot. Never inside the app shell.
                                0:00 / 2:05
                                HTML
                                <div class="cn-video-player" data-variant="mockup" data-size="lg" data-state="paused" role="region" aria-label="Video: Introducing Lumen reporting"><svg class="cn-video-player__media" viewBox="0 0 640 360" role="img" aria-label="Poster: the Lumen reporting workspace"><rect width="640" height="360" style="fill:var(--cn-color-bg-inverse)"/><rect x="72" y="56" width="496" height="248" rx="16" style="fill:var(--cn-color-fg-inverse);opacity:.08"/><rect x="96" y="80" width="140" height="10" rx="5" style="fill:var(--cn-color-fg-inverse);opacity:.32"/><rect x="96" y="100" width="88" height="8" rx="4" style="fill:var(--cn-color-fg-inverse);opacity:.18"/><rect x="96" y="132" width="212" height="148" rx="10" style="fill:var(--cn-color-fg-inverse);opacity:.1"/><rect x="332" y="132" width="212" height="64" rx="10" style="fill:var(--cn-color-fg-inverse);opacity:.1"/><rect x="332" y="216" width="212" height="64" rx="10" style="fill:var(--cn-color-fg-inverse);opacity:.1"/><path d="M116 250l40-32 38 18 44-52 42 20 28-36" fill="none" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" style="stroke:var(--cn-color-bg-accent)"/></svg><button type="button" class="cn-video-player__play" aria-label="Play"><svg class="cn-icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M5 3.2v9.6L12.6 8z"/></svg></button><div class="cn-video-player__controls"><div class="cn-video-player__progress" role="slider" aria-label="Seek" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" tabindex="0"><div class="cn-video-player__progress-fill" style="width:0%"></div><span class="cn-video-player__scrubber" style="left:0%"></span></div><div class="cn-video-player__row"><div class="cn-video-player__group"><button type="button" class="cn-video-player__button" aria-label="Play"><svg class="cn-icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M5 3.2v9.6L12.6 8z"/></svg></button><button type="button" class="cn-video-player__button" aria-label="Mute"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 6.25h2.25L8 3.5v9L4.75 9.75H2.5z"/><path d="M10.5 5.75a3 3 0 010 4.5M12.25 4a5.5 5.5 0 010 8"/></svg></button><span class="cn-video-player__time">0:00 / 2:05</span></div><div class="cn-video-player__group"><button type="button" class="cn-video-player__button" aria-label="Captions" aria-pressed="false"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="9" rx="1.5"/><path d="M5 8.75h2.5M8.75 8.75H11M5 6.25h6"/></svg></button><button type="button" class="cn-video-player__button" aria-label="Settings"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg></button><button type="button" class="cn-video-player__button" aria-label="Full screen"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6.5v-3.5h3.5M13 6.5v-3.5H9.5M3 9.5v3.5h3.5M13 9.5v3.5H9.5"/></svg></button></div></div></div></div>

                                Matrix

                                variant ↓ / size →smmdlg
                                default
                                0:42 / 3:18
                                0:42 / 3:18
                                0:42 / 3:18
                                minimal
                                0:42 / 3:18
                                0:42 / 3:18
                                0:42 / 3:18
                                mockup
                                0:42 / 3:18
                                0:42 / 3:18
                                0:42 / 3:18
                                Anatomy, props, states, rules

                                Anatomy

                                • .cn-video-player <div> — The frame: relative, 16:9, bg-inverse, radius card, overflow hidden. data-state="paused|playing". Max width from size.
                                • .cn-video-player__media <video> — The <video> (with poster) or an <img>, absolutely filling the frame with object-fit cover. In the gallery an inline svg poster.
                                • .cn-video-player__play <button> — Centered 64px circle, bg-surface at 90%, shadow-lg, with a 24px play icon. Hidden while playing.
                                • .cn-video-player__controls <div> — Bottom bar on a scrim (bg-inverse at 70%): the progress line, then the row of buttons and time.
                                • .cn-video-player__progress <div> — 2px full-width track (fg-inverse at 30%), role="slider" for seeking; grows to 4px on hover.
                                • .cn-video-player__progress-fill <div> — The played portion in fg-inverse; width is the inline percentage.
                                • .cn-video-player__scrubber <span> — 12px round handle at the end of the fill (left = the same percentage).
                                • .cn-video-player__row <div> — The controls row: a left group (play, volume, time) and a right group (captions, settings, full screen).
                                • .cn-video-player__group <div> — A cluster of buttons with a 4px gap.
                                • .cn-video-player__button <button> — 32px icon button in fg-inverse, radius md, translucent hover. aria-label required; captions is a toggle with aria-pressed.
                                • .cn-video-player__time <span> — Elapsed / total in code-sm fg-inverse, tabular ("0:42 / 3:18").

                                Props

                                propvalues (default outlined)meaning
                                variantdefault minimal mockupdefault = play button plus the full control bar (in-app video). minimal = poster and play button only; controls appear once playing (embeds in cards and docs). mockup = the default chrome inside a framed, shadowed device-like shot for marketing hero sections; never inside the app shell.
                                sizesm md lgMaximum width of the frame: sm 480px (cards, side panels), md 720px (content pages), lg 960px (marketing sections). Always 100% wide below that.

                                States

                                stateselectormeaning
                                playing[data-state="playing"]Media is playing: the centered play button hides and the bar shows a pause icon. The app toggles the attribute. (data-state="playing" on the root)
                                paused[data-state="paused"]Media is paused or not started: the centered play button is visible over the poster. (data-state="paused" on the root (default))
                                playHover .cn-video-player__play:hoverPointer over the big play button (extraCss): the circle becomes fully opaque. (native :hover on .cn-video-player__play)
                                buttonHover .cn-video-player__button:hoverPointer over a bar button (extraCss): a translucent fg-inverse fill. (native :hover on .cn-video-player__button)
                                buttonFocus .cn-video-player__button:focus-visibleKeyboard focus on a bar button or the play button shows the focus ring (extraCss). (native :focus-visible on .cn-video-player__button)
                                buttonPressed .cn-video-player__button[aria-pressed="true"]A toggle such as captions that is on (extraCss): keeps the translucent fill at rest. (aria-pressed="true" on .cn-video-player__button)

                                Rules

                                • Always 16:9 unless the source is vertical; the frame keeps its ratio while loading so the page does not jump.
                                • Show a real poster frame, never a black rectangle; the big play button is the only overlay on it.
                                • The bar holds at most six controls: play/pause, volume, time on the left; captions, settings, full screen on the right. Extra features go in the settings menu.
                                • The time label is "elapsed / total" in the mono style with tabular figures; show a live stream as "LIVE" with a red dot instead.
                                • Hide the bar 3 seconds after the pointer leaves while playing; show it on hover, focus or touch. Never hide it while paused.
                                • Never autoplay with sound. Autoplay muted only for decorative loops on marketing pages, with a visible play/pause control.
                                • sm inside cards and side panels, md on content pages, lg in marketing sections; mockup only on marketing pages.
                                • Captions are a toggle (aria-pressed) and default to on when the viewer's system prefers captions.

                                Accessibility

                                • Root is a labelled region; the <video> has a title and, for real content, captions (WebVTT) and an audio description track when available.
                                • Every control is a <button> with an aria-label; play/pause changes its label with the state and captions uses aria-pressed.
                                • The progress line is role="slider" with aria-valuemin/max/now and a text alternative for the time; Left/Right seek 5 seconds, Home/End jump.
                                • Keyboard: Space or K toggles play, M mutes, F enters full screen, C toggles captions; Escape leaves full screen.
                                • Focus rings stay visible on the dark chrome (the ring color is independent of the theme); never remove outline without the box-shadow ring.
                                Application layouts

                                App shell

                                Top navigation bar with 4–5 tabs phrased as user questions, a page header (kicker + title + one primary action), and a content area with a max width. No left sidebar by default.

                                Product previewPrimary layout
                                1280 × 760
                                Hoje

                                O que precisa de mim?

                                3 aprovações e 2 respostas aguardando.

                                Content
                                HTML
                                <div class="cn-shell"><header class="cn-topbar"><div class="cn-topbar__inner"><a href="#" class="cn-topbar__brand">Vera</a><nav class="cn-tabs" data-variant="underline" data-size="md" aria-label="Main"><a class="cn-tabs__tab" href="#" aria-current="page">O que precisa de mim?</a><a class="cn-tabs__tab" href="#">A equipe está trabalhando?</a><a class="cn-tabs__tab" href="#">Onde cada pessoa está?</a><a class="cn-tabs__tab" href="#">Configurações</a></nav><div class="cn-topbar__actions"><span class="cn-avatar" data-size="sm" data-shape="circle"><span class="cn-avatar__fallback">MD</span></span></div></div></header><main class="cn-page"><div class="cn-page__header"><div><div class="cn-kicker" data-mark="square"><span class="cn-kicker__mark"></span>Hoje</div><h1 class="cn-page__title">O que precisa de mim?</h1><p class="cn-page__description">3 aprovações e 2 respostas aguardando.</p></div><div class="cn-page__actions"><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Exportar</span></button><button type="button" class="cn-button" data-variant="primary" data-size="md"><span class="cn-button__label">Nova busca</span></button></div></div><div class="cn-page__body"><section class="cn-card" data-variant="default" data-padding="md"><div class="cn-card__body cn-text-body-md">Content</div></section></div></main></div>
                                Layout guidelines
                                • Structure: <header class="cn-topbar"> → <main class="cn-page"> → <div class="cn-page__header"> + <div class="cn-page__body">.
                                • Page header: kicker (mono uppercase) above the h1 (heading-xl). Actions on the right: at most one primary button plus ghost/outline buttons.
                                • Content max width 1152px (size.container.xl), 24px side padding, 32px between page header and body.
                                • Sections inside the body are separated by 32px (space.8); groups inside a section by 16px (space.4); rows inside a group by 8px (space.2).
                                • Detail views open in a Drawer over the current list; do not navigate to a separate page for one item.
                                Application sections

                                Form layout

                                Vertical form: one column, labels above fields, 16px between fields, sections separated by a divider with a heading, actions at the end aligned right.

                                Product previewPrimary layout
                                1280 × 760

                                Empresa

                                Como a Vera deve se apresentar.

                                Como aparece na Receita Federal.

                                HTML
                                <form class="cn-form" style="max-width:520px"><section class="cn-form__section"><h3 class="cn-form__title">Empresa</h3><p class="cn-form__description">Como a Vera deve se apresentar.</p><div class="cn-field"><label class="cn-field__label" for="f1">Nome da empresa</label><div class="cn-input" data-variant="default" data-size="md"><input id="f1" class="cn-input__field" type="text" placeholder="Vero Finance"></div><p class="cn-field__helper">Como aparece na Receita Federal.</p></div><div class="cn-form__row"><div class="cn-field"><label class="cn-field__label" for="f2">Cidade</label><div class="cn-input" data-variant="default" data-size="md"><input id="f2" class="cn-input__field" type="text"></div></div><div class="cn-field"><label class="cn-field__label" for="f3">UF</label><div class="cn-input" data-variant="default" data-size="md"><input id="f3" class="cn-input__field" type="text" maxlength="2"></div></div></div></section><div class="cn-form__actions"><button type="button" class="cn-button" data-variant="ghost" data-size="md"><span class="cn-button__label">Cancelar</span></button><button type="submit" class="cn-button" data-variant="primary" data-size="md"><span class="cn-button__label">Salvar</span></button></div></form>
                                Layout guidelines
                                • One column. Two columns only for short related pairs (city/state, first/last name) using .cn-form__row.
                                • Field order: label → control → helper or error. Helper and error never show at the same time.
                                • Required is the default; mark optional fields with "(optional)" in the label instead of asterisks.
                                • Gap between fields 16px (space.4); between sections 32px (space.8) with a section title (heading-sm) and optional description.
                                • Actions: right-aligned, ghost "Cancel" then primary. In a dialog they live in the dialog footer; in a page they are sticky at the bottom only when the form is long.
                                • Max width of a form column: 640px (size.container.sm).
                                Application pages

                                Audit log and activity

                                A chronological record of who did what: a dense table with actor, action, target and metadata and expandable detail rows; a timeline view; and a security-events variant with severity.

                                Product previewPrimary layout
                                1280 × 760

                                Audit log

                                Every change in your workspace, kept for 400 days.

                                1,204 events
                                ActorEventCategoryWhen
                                Maya Chen
                                updated Billing settings · Changed payment method to Visa •••• 4242settingsToday 09:41
                                Event ID
                                evt_01J8Z3K2QWERTY
                                IP address
                                187.34.12.9 · Florianópolis, BR
                                User agent
                                Chrome 129 · macOS
                                Before → After
                                Mastercard •••• 0913 → Visa •••• 4242
                                Daniel Costa
                                deployed Billing webhook v2 · Production · 2 migrationsdeployToday 08:20
                                System
                                rotated API key lum_live_…8f2a · Scheduled rotation (90 days)securityToday 03:00
                                Sofia Almeida
                                shared Brand assets v3 · With 8 people · view onlyfilesYesterday 18:40
                                Aisha Khan
                                changed role Elena Rossi · Member → AdminmembersYesterday 16:12
                                Event ID
                                evt_01J8Z3K2QWERTY
                                IP address
                                187.34.12.9 · Florianópolis, BR
                                User agent
                                Chrome 129 · macOS
                                Before → After
                                Mastercard •••• 0913 → Visa •••• 4242
                                Lucas Ferreira
                                exported Customers (2,420 rows) · CSV · 1.2 MBdataMon 11:05
                                Noah Berg
                                signed in New device · MacBook Pro · São PaulosecurityMon 09:02
                                Page 1 of 10
                                HTML
                                <div class="cn-app"><nav class="cn-sidebar" data-variant="simple" data-theme="light" aria-label="Main"><div class="cn-sidebar__brand"><span class="cn-sidebar__logo" aria-hidden="true"></span><span class="cn-sidebar__brand-name">Lumen</span></div><div class="cn-sidebar__search cn-input" data-variant="default" data-size="md"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search" aria-label="Search"></div><div class="cn-sidebar__nav"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg><span class="cn-sidebar__label">Home</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><span class="cn-sidebar__label">Dashboard</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-sidebar__label">Projects</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-sidebar__label">Tasks</span><span class="cn-sidebar__badge">10</span></a><a href="#" class="cn-sidebar__item" aria-current="page"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-sidebar__label">Reporting</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><span class="cn-sidebar__label">Users</span></a></div><div class="cn-sidebar__footer"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg><span class="cn-sidebar__label">Support</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg><span class="cn-sidebar__label">Settings</span></a><div class="cn-sidebar__account"><span class="cn-sidebar__avatar" aria-hidden="true">MC</span><div class="cn-sidebar__account-text"><span class="cn-sidebar__name">Maya Chen</span><span class="cn-sidebar__email">maya@lumen.co</span></div><button type="button" class="cn-sidebar__account-action" aria-label="Log out"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button></div></div></nav><main class="cn-app__main"><header class="cn-page-header" data-variant="simple" data-divider="yes"><div class="cn-page-header__content"><h1 class="cn-page-header__title">Audit log</h1><p class="cn-page-header__description">Every change in your workspace, kept for 400 days.</p></div><div class="cn-page-header__actions"><button type="button" class="cn-button" data-variant="outline" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg><span class="cn-button__label">Retention settings</span></button></div></header><div class="cn-row" data-justify="between" data-gap="3" data-wrap><div class="cn-row" data-gap="3" data-wrap><div class="cn-input" data-variant="default" data-size="md" style="width:280px"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search events" aria-label="Search events"></div><button type="button" class="cn-button" data-variant="outline" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg><span class="cn-button__label">Actor</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg><span class="cn-button__label">Category</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><span class="cn-button__label">Last 7 days</span></button></div><div class="cn-row" data-gap="2"><span class="cn-text-body-sm cn-app__muted">1,204 events</span><button type="button" class="cn-button" data-variant="outline" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg><span class="cn-button__label">Export</span></button></div></div><section class="cn-card cn-app__table-card" data-variant="default" data-padding="none"><div class="cn-card__body cn-app__card-body cn-app__card-body--flush" role="region" tabindex="0" aria-label="Scrollable table"><table class="cn-table" data-density="compact" data-variant="default" data-interactive><thead class="cn-table__head"><tr class="cn-table__row"><th class="cn-table__header-cell" scope="col" style="width:32px"></th><th class="cn-table__header-cell" scope="col">Actor</th><th class="cn-table__header-cell" scope="col">Event</th><th class="cn-table__header-cell" scope="col">Category</th><th class="cn-table__header-cell" scope="col" data-align="right">When</th></tr></thead><tbody class="cn-table__body"><tr class="cn-table__row" data-selected><td class="cn-table__cell"><button type="button" class="cn-audit__toggle" aria-expanded="true" aria-label="Details"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button></td><td class="cn-table__cell"><div class="cn-row" data-gap="2"><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="accent" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span><span class="cn-text-label-sm">Maya Chen</span></div></td><td class="cn-table__cell"><span class="cn-app__muted">updated</span> <span class="cn-text-label-sm">Billing settings</span> <span class="cn-app__muted">· Changed payment method to Visa •••• 4242</span></td><td class="cn-table__cell"><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">settings</span></td><td class="cn-table__cell" data-align="right">Today 09:41</td></tr><tr class="cn-table__row cn-audit__detail"><td class="cn-table__cell" colspan="5"><dl class="cn-audit__dl"><div><dt>Event ID</dt><dd><code class="cn-code" data-variant="inline" data-size="sm">evt_01J8Z3K2QWERTY</code></dd></div><div><dt>IP address</dt><dd>187.34.12.9 · Florianópolis, BR</dd></div><div><dt>User agent</dt><dd>Chrome 129 · macOS</dd></div><div><dt>Before → After</dt><dd>Mastercard •••• 0913 → Visa •••• 4242</dd></div></dl></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><button type="button" class="cn-audit__toggle" aria-expanded="false" aria-label="Details"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button></td><td class="cn-table__cell"><div class="cn-row" data-gap="2"><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="success" data-status="none" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span></span><span class="cn-text-label-sm">Daniel Costa</span></div></td><td class="cn-table__cell"><span class="cn-app__muted">deployed</span> <span class="cn-text-label-sm">Billing webhook v2</span> <span class="cn-app__muted">· Production · 2 migrations</span></td><td class="cn-table__cell"><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">deploy</span></td><td class="cn-table__cell" data-align="right">Today 08:20</td></tr><tr class="cn-table__row"><td class="cn-table__cell"><button type="button" class="cn-audit__toggle" aria-expanded="false" aria-label="Details"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button></td><td class="cn-table__cell"><div class="cn-row" data-gap="2"><span class="cn-audit__sys" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg></span><span class="cn-text-label-sm">System</span></div></td><td class="cn-table__cell"><span class="cn-app__muted">rotated</span> <span class="cn-text-label-sm">API key lum_live_…8f2a</span> <span class="cn-app__muted">· Scheduled rotation (90 days)</span></td><td class="cn-table__cell"><span class="cn-badge" data-tone="warning" data-variant="soft" data-size="sm">security</span></td><td class="cn-table__cell" data-align="right">Today 03:00</td></tr><tr class="cn-table__row"><td class="cn-table__cell"><button type="button" class="cn-audit__toggle" aria-expanded="false" aria-label="Details"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button></td><td class="cn-table__cell"><div class="cn-row" data-gap="2"><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="warning" data-status="none" role="img" aria-label="Sofia Almeida"><span class="cn-avatar__fallback" aria-hidden="true">SA</span></span><span class="cn-text-label-sm">Sofia Almeida</span></div></td><td class="cn-table__cell"><span class="cn-app__muted">shared</span> <span class="cn-text-label-sm">Brand assets v3</span> <span class="cn-app__muted">· With 8 people · view only</span></td><td class="cn-table__cell"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">files</span></td><td class="cn-table__cell" data-align="right">Yesterday 18:40</td></tr><tr class="cn-table__row" data-selected><td class="cn-table__cell"><button type="button" class="cn-audit__toggle" aria-expanded="true" aria-label="Details"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button></td><td class="cn-table__cell"><div class="cn-row" data-gap="2"><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="accent" data-status="none" role="img" aria-label="Aisha Khan"><span class="cn-avatar__fallback" aria-hidden="true">AK</span></span><span class="cn-text-label-sm">Aisha Khan</span></div></td><td class="cn-table__cell"><span class="cn-app__muted">changed role</span> <span class="cn-text-label-sm">Elena Rossi</span> <span class="cn-app__muted">· Member → Admin</span></td><td class="cn-table__cell"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">members</span></td><td class="cn-table__cell" data-align="right">Yesterday 16:12</td></tr><tr class="cn-table__row cn-audit__detail"><td class="cn-table__cell" colspan="5"><dl class="cn-audit__dl"><div><dt>Event ID</dt><dd><code class="cn-code" data-variant="inline" data-size="sm">evt_01J8Z3K2QWERTY</code></dd></div><div><dt>IP address</dt><dd>187.34.12.9 · Florianópolis, BR</dd></div><div><dt>User agent</dt><dd>Chrome 129 · macOS</dd></div><div><dt>Before → After</dt><dd>Mastercard •••• 0913 → Visa •••• 4242</dd></div></dl></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><button type="button" class="cn-audit__toggle" aria-expanded="false" aria-label="Details"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button></td><td class="cn-table__cell"><div class="cn-row" data-gap="2"><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="info" data-status="none" role="img" aria-label="Lucas Ferreira"><span class="cn-avatar__fallback" aria-hidden="true">LF</span></span><span class="cn-text-label-sm">Lucas Ferreira</span></div></td><td class="cn-table__cell"><span class="cn-app__muted">exported</span> <span class="cn-text-label-sm">Customers (2,420 rows)</span> <span class="cn-app__muted">· CSV · 1.2 MB</span></td><td class="cn-table__cell"><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">data</span></td><td class="cn-table__cell" data-align="right">Mon 11:05</td></tr><tr class="cn-table__row"><td class="cn-table__cell"><button type="button" class="cn-audit__toggle" aria-expanded="false" aria-label="Details"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button></td><td class="cn-table__cell"><div class="cn-row" data-gap="2"><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="success" data-status="none" role="img" aria-label="Noah Berg"><span class="cn-avatar__fallback" aria-hidden="true">NB</span></span><span class="cn-text-label-sm">Noah Berg</span></div></td><td class="cn-table__cell"><span class="cn-app__muted">signed in</span> <span class="cn-text-label-sm">New device</span> <span class="cn-app__muted">· MacBook Pro · São Paulo</span></td><td class="cn-table__cell"><span class="cn-badge" data-tone="warning" data-variant="soft" data-size="sm">security</span></td><td class="cn-table__cell" data-align="right">Mon 09:02</td></tr></tbody></table></div><footer class="cn-card__footer"><div class="cn-row" data-justify="between" data-gap="3"><span class="cn-text-body-sm cn-app__muted">Page 1 of 10</span><div class="cn-row" data-gap="2"><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Previous</span></button><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Next</span></button></div></div></footer></section></main></div>
                                Layout guidelines
                                • Filters: search, actor, category and a date range on the left; count and Export on the right. Filters never hide the table.
                                • Rows are compact (48px): actor (avatar xs + name), action in plain words, target in label-sm, a category badge, timestamp right-aligned in body-sm muted; important rows can expand to a detail block (key/value list) below the row.
                                • Timeline variant groups by day with a vertical line and dots; the same fields, more room for descriptions.
                                • Security events use severity badges (danger/warning/neutral) and a “Review” action; never color entire rows.
                                Application pages

                                Billing and checkout

                                Plan and usage, payment method, billing history; a plan change with a comparison; and a checkout form with an order summary.

                                Product previewPrimary layout
                                1280 × 760

                                Billing

                                Manage your plan, payment method and invoices.

                                Business plan Monthly

                                Renews on 12 October 2026

                                $20 per user / month14 of 20 seats used

                                Payment method

                                Change how you pay for your plan.

                                Visa ending in 4242Expiry 06/2028 · billing@lumen.co
                                Default

                                Billing history

                                Download invoices for your records.

                                InvoiceDateStatusAmountSeatsActions
                                Invoice #2091Sep 2026Paid$280.0014
                                Invoice #2090Aug 2026Paid$260.0013
                                Invoice #2089Jul 2026Paid$240.0012
                                Invoice #2088Jun 2026Refunded$220.0011
                                Invoice #2087May 2026Failed$220.0011
                                HTML
                                <div class="cn-app"><nav class="cn-sidebar" data-variant="simple" data-theme="light" aria-label="Main"><div class="cn-sidebar__brand"><span class="cn-sidebar__logo" aria-hidden="true"></span><span class="cn-sidebar__brand-name">Lumen</span></div><div class="cn-sidebar__search cn-input" data-variant="default" data-size="md"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search" aria-label="Search"></div><div class="cn-sidebar__nav"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg><span class="cn-sidebar__label">Home</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><span class="cn-sidebar__label">Dashboard</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-sidebar__label">Projects</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-sidebar__label">Tasks</span><span class="cn-sidebar__badge">10</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-sidebar__label">Reporting</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><span class="cn-sidebar__label">Users</span></a></div><div class="cn-sidebar__footer"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg><span class="cn-sidebar__label">Support</span></a><a href="#" class="cn-sidebar__item" aria-current="page"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg><span class="cn-sidebar__label">Settings</span></a><div class="cn-sidebar__account"><span class="cn-sidebar__avatar" aria-hidden="true">MC</span><div class="cn-sidebar__account-text"><span class="cn-sidebar__name">Maya Chen</span><span class="cn-sidebar__email">maya@lumen.co</span></div><button type="button" class="cn-sidebar__account-action" aria-label="Log out"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button></div></div></nav><main class="cn-app__main"><header class="cn-page-header" data-variant="simple" data-divider="yes"><div class="cn-page-header__content"><h1 class="cn-page-header__title">Billing</h1><p class="cn-page-header__description">Manage your plan, payment method and invoices.</p></div><div class="cn-page-header__actions"><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Contact billing support</span></button></div></header><div class="cn-tabs" data-variant="underline" data-size="md" role="tablist"><button type="button" class="cn-tabs__tab" role="tab" aria-selected="true">Plan</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">Usage</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">Invoices</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">Tax</button></div><div class="cn-alert" data-tone="danger" data-variant="soft" data-layout="inline" role="alert"><span class="cn-alert__icon" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 2.5l6 11H2l6-11zM8 7v3M8 11.5v.5"/></svg></span><div class="cn-alert__content"><div class="cn-alert__title">Your last payment failed</div><p class="cn-alert__description">We couldn’t charge the Visa ending in 4242 on 12 September. Update your payment method to keep your plan active.</p><div class="cn-alert__actions"><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Update payment method</span></button></div></div></div><div class="cn-grid" data-cols="2" data-gap="6"><section class="cn-card" data-variant="default" data-padding="none"><div class="cn-section-header" data-variant="card" data-divider="yes"><div class="cn-section-header__content"><h2 class="cn-section-header__title">Business plan <span class="cn-section-header__badge"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Monthly</span></span></h2><p class="cn-section-header__description">Renews on 12 October 2026</p></div><div class="cn-section-header__actions"><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Change plan</span></button></div></div><div class="cn-card__body cn-app__card-body cn-stack" data-gap="4"><div class="cn-row" data-justify="between" data-align="end"><span class="cn-text-heading-lg">$20<span class="cn-text-body-md cn-app__muted"> per user / month</span></span><span class="cn-text-body-sm cn-app__muted">14 of 20 seats used</span></div><div class="cn-progress" data-size="sm" data-tone="action" role="progressbar" aria-valuenow="70" aria-valuemin="0" aria-valuemax="100" aria-label="Seats used"><div class="cn-progress__track"><div class="cn-progress__indicator" style="width:70%"></div></div></div></div></section><section class="cn-card" data-variant="default" data-padding="none"><div class="cn-section-header" data-variant="card" data-divider="yes"><div class="cn-section-header__content"><h2 class="cn-section-header__title">Payment method</h2><p class="cn-section-header__description">Change how you pay for your plan.</p></div><div class="cn-section-header__actions"><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Edit</span></button></div></div><div class="cn-card__body cn-app__card-body"><div class="cn-billing__card"><span class="cn-billing__brand" aria-hidden="true">VISA</span><div class="cn-stack cn-flex-1" data-gap="0"><span class="cn-text-label-sm">Visa ending in 4242</span><span class="cn-text-body-sm cn-app__muted">Expiry 06/2028 · billing@lumen.co</span></div><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">Default</span></div></div></section></div><section class="cn-card cn-app__table-card" data-variant="default" data-padding="none"><div class="cn-section-header" data-variant="card" data-divider="yes"><div class="cn-section-header__content"><h2 class="cn-section-header__title">Billing history</h2><p class="cn-section-header__description">Download invoices for your records.</p></div><div class="cn-section-header__actions"><button type="button" class="cn-button" data-variant="outline" data-size="sm"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg><span class="cn-button__label">Download all</span></button></div></div><div class="cn-card__body cn-app__card-body cn-app__card-body--flush" role="region" tabindex="0" aria-label="Scrollable table"><table class="cn-table" data-density="default" data-variant="default"><thead class="cn-table__head"><tr class="cn-table__row"><th class="cn-table__header-cell" scope="col">Invoice</th><th class="cn-table__header-cell" scope="col">Date</th><th class="cn-table__header-cell" scope="col">Status</th><th class="cn-table__header-cell" scope="col" data-align="right">Amount</th><th class="cn-table__header-cell" scope="col" data-align="right">Seats</th><th class="cn-table__header-cell" scope="col"><span class="cn-sr-only">Actions</span></th></tr></thead><tbody class="cn-table__body"><tr class="cn-table__row"><td class="cn-table__cell"><span class="cn-text-label-sm">Invoice #2091</span></td><td class="cn-table__cell">Sep 2026</td><td class="cn-table__cell"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Paid</span></td><td class="cn-table__cell" data-align="right">$280.00</td><td class="cn-table__cell" data-align="right">14</td><td class="cn-table__cell cn-table__actions"><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Download</span></button></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><span class="cn-text-label-sm">Invoice #2090</span></td><td class="cn-table__cell">Aug 2026</td><td class="cn-table__cell"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Paid</span></td><td class="cn-table__cell" data-align="right">$260.00</td><td class="cn-table__cell" data-align="right">13</td><td class="cn-table__cell cn-table__actions"><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Download</span></button></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><span class="cn-text-label-sm">Invoice #2089</span></td><td class="cn-table__cell">Jul 2026</td><td class="cn-table__cell"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Paid</span></td><td class="cn-table__cell" data-align="right">$240.00</td><td class="cn-table__cell" data-align="right">12</td><td class="cn-table__cell cn-table__actions"><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Download</span></button></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><span class="cn-text-label-sm">Invoice #2088</span></td><td class="cn-table__cell">Jun 2026</td><td class="cn-table__cell"><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Refunded</span></td><td class="cn-table__cell" data-align="right">$220.00</td><td class="cn-table__cell" data-align="right">11</td><td class="cn-table__cell cn-table__actions"><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Download</span></button></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><span class="cn-text-label-sm">Invoice #2087</span></td><td class="cn-table__cell">May 2026</td><td class="cn-table__cell"><span class="cn-badge" data-tone="danger" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Failed</span></td><td class="cn-table__cell" data-align="right">$220.00</td><td class="cn-table__cell" data-align="right">11</td><td class="cn-table__cell cn-table__actions"><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Download</span></button></td></tr></tbody></table></div></section></main></div>
                                Layout guidelines
                                • Billing page: plan card (name, renewal, price per seat, seat usage bar), payment method card (brand mark, last 4, expiry, Default badge), invoices table (number, date, status badge, amount right-aligned, download link).
                                • Failed payments show a danger Alert at the top with a single "Update payment method" action.
                                • Checkout: two columns — form (contact, card number with brand icon, expiry + CVC in a row, country) on the left, an order summary card on the right (line items, subtotal, tax, total in heading-md); the primary button names the amount ("Pay $280.00").
                                • Never mask everything: show the last 4 digits and the brand; never ask for the CVC outside the payment step.
                                Application pages

                                Calendar and scheduling

                                Month grid with event chips, week view with time blocks, and an agenda list with an event detail card.

                                Product previewPrimary layout
                                1280 × 760

                                September 2026

                                MonTueWedThuFriSatSun
                                31
                                1
                                29:00Design sync
                                310:30Roadmap review15:00Customer call
                                4
                                5
                                6
                                7
                                811:00Sprint planning
                                9
                                1014:00Billing demo
                                11
                                12
                                13
                                149:30Hiring loop13:00Lunch & learn16:00Retro
                                15
                                16
                                1710:00All hands
                                18
                                19
                                20
                                21
                                229:00Design sync
                                23
                                24
                                2515:00Launch go/no-go
                                26
                                27
                                28
                                29
                                30
                                1
                                2
                                3
                                4
                                HTML
                                <div class="cn-app"><nav class="cn-sidebar" data-variant="simple" data-theme="light" aria-label="Main"><div class="cn-sidebar__brand"><span class="cn-sidebar__logo" aria-hidden="true"></span><span class="cn-sidebar__brand-name">Lumen</span></div><div class="cn-sidebar__search cn-input" data-variant="default" data-size="md"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search" aria-label="Search"></div><div class="cn-sidebar__nav"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg><span class="cn-sidebar__label">Home</span></a><a href="#" class="cn-sidebar__item" aria-current="page"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><span class="cn-sidebar__label">Dashboard</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-sidebar__label">Projects</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-sidebar__label">Tasks</span><span class="cn-sidebar__badge">10</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-sidebar__label">Reporting</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><span class="cn-sidebar__label">Users</span></a></div><div class="cn-sidebar__footer"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg><span class="cn-sidebar__label">Support</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg><span class="cn-sidebar__label">Settings</span></a><div class="cn-sidebar__account"><span class="cn-sidebar__avatar" aria-hidden="true">MC</span><div class="cn-sidebar__account-text"><span class="cn-sidebar__name">Maya Chen</span><span class="cn-sidebar__email">maya@lumen.co</span></div><button type="button" class="cn-sidebar__account-action" aria-label="Log out"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button></div></div></nav><main class="cn-app__main"><header class="cn-page-header" data-variant="simple" data-divider="yes"><div class="cn-page-header__content"><h1 class="cn-page-header__title">September 2026</h1></div><div class="cn-page-header__actions"><div class="cn-segmented-control" data-size="md" data-variant="default" role="radiogroup" aria-label="View"><button type="button" class="cn-segmented-control__item" role="radio" aria-checked="true">Month</button><button type="button" class="cn-segmented-control__item" role="radio" aria-checked="false">Week</button><button type="button" class="cn-segmented-control__item" role="radio" aria-checked="false">Day</button></div><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Today</span></button><div class="cn-button-group" data-variant="attached" data-orientation="horizontal"><button type="button" class="cn-button" data-variant="outline" data-size="md" data-icon="only" aria-label="Previous month"><svg style="transform:rotate(180deg)" class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button><button type="button" class="cn-button" data-variant="outline" data-size="md" data-icon="only" aria-label="Next month"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></button></div><button type="button" class="cn-button" data-variant="primary" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg><span class="cn-button__label">New event</span></button></div></header><div class="cn-cal"><div class="cn-cal__weekdays"><span>Mon</span><span>Tue</span><span>Wed</span><span>Thu</span><span>Fri</span><span>Sat</span><span>Sun</span></div><div class="cn-cal__grid"><div class="cn-cal__cell" data-outside><span class="cn-cal__daynum">31</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">1</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">2</span><span class="cn-cal__event" data-tone="accent"><span class="cn-cal__time">9:00</span>Design sync</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">3</span><span class="cn-cal__event" data-tone="info"><span class="cn-cal__time">10:30</span>Roadmap review</span><span class="cn-cal__event" data-tone="success"><span class="cn-cal__time">15:00</span>Customer call</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">4</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">5</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">6</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">7</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">8</span><span class="cn-cal__event" data-tone="accent"><span class="cn-cal__time">11:00</span>Sprint planning</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">9</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">10</span><span class="cn-cal__event" data-tone="warning"><span class="cn-cal__time">14:00</span>Billing demo</span></div><div class="cn-cal__cell" data-today><span class="cn-cal__daynum">11</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">12</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">13</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">14</span><span class="cn-cal__event" data-tone="info"><span class="cn-cal__time">9:30</span>Hiring loop</span><span class="cn-cal__event" data-tone="success"><span class="cn-cal__time">13:00</span>Lunch & learn</span><span class="cn-cal__event" data-tone="accent"><span class="cn-cal__time">16:00</span>Retro</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">15</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">16</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">17</span><span class="cn-cal__event" data-tone="accent"><span class="cn-cal__time">10:00</span>All hands</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">18</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">19</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">20</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">21</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">22</span><span class="cn-cal__event" data-tone="accent"><span class="cn-cal__time">9:00</span>Design sync</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">23</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">24</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">25</span><span class="cn-cal__event" data-tone="danger"><span class="cn-cal__time">15:00</span>Launch go/no-go</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">26</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">27</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">28</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">29</span></div><div class="cn-cal__cell"><span class="cn-cal__daynum">30</span></div><div class="cn-cal__cell" data-outside><span class="cn-cal__daynum">1</span></div><div class="cn-cal__cell" data-outside><span class="cn-cal__daynum">2</span></div><div class="cn-cal__cell" data-outside><span class="cn-cal__daynum">3</span></div><div class="cn-cal__cell" data-outside><span class="cn-cal__daynum">4</span></div></div></div></main></div>
                                Layout guidelines
                                • Header: month/year title with prev/next icon buttons and a "Today" outline button; view switch (Month / Week / Day) as a segmented control; "New event" is the primary action.
                                • Month grid: 7 columns, cells ≥ 120px tall, day number top-left (label-sm), events as small chips (body-xs, 4px radius, tinted by category); outside-month days muted; today has a brand-filled day number.
                                • Week view: time column + 5–7 day columns, 1-hour rows (64px), events as blocks with a tinted background and left border; overlapping events share the column.
                                • Agenda: list grouped by day; each item time (code-sm) + title + attendees avatars; clicking opens the detail card (title, time, location, attendees, actions).
                                Application pages

                                Dashboard

                                Application home: sidebar, page header with the primary action, a metrics row, a chart card with an activity list, and a data table card. Three layouts.

                                Product previewPrimary layout
                                1280 × 760

                                Welcome back, Maya

                                Track, manage and forecast your customers and orders.

                                Total customers
                                2,420
                                +40%
                                vs last month
                                Members
                                1,210
                                +10%
                                vs last month
                                Active now
                                316
                                −2%
                                vs last month
                                Conversion
                                4.2%
                                +0.4%
                                vs last month

                                Revenue

                                Monthly recurring revenue, last 12 months

                                $128,430This yearLast year

                                Recent activity

                                • Maya Chencommented on Q3 roadmap2m
                                • Daniel Costamerged “Billing refactor”14m
                                • Sofia Almeidauploaded 3 files to Brand1h
                                • Lucas Ferreirainvited Elena Rossi3h

                                Team members 100 users

                                Manage your team members and their account permissions here.

                                NameStatusRoleEmail addressTeamsActions
                                Maya Chen@maya
                                ActiveProduct Managermaya@lumen.co
                                DesignProduct
                                Daniel Costa@daniel
                                ActiveEngineeringdaniel@lumen.co
                                DesignProduct+2
                                Sofia Almeida@sofia
                                OfflineDesignsofia@lumen.co
                                DesignProduct
                                Lucas Ferreira@lucas
                                ActiveFrontendlucas@lumen.co
                                DesignProduct+2
                                Aisha Khan@aisha
                                ActiveBackendaisha@lumen.co
                                DesignProduct
                                Noah Berg@noah
                                OfflineProduct Designnoah@lumen.co
                                DesignProduct+2
                                Page 1 of 10
                                HTML
                                <div class="cn-app"><nav class="cn-sidebar" data-variant="simple" data-theme="light" aria-label="Main"><div class="cn-sidebar__brand"><span class="cn-sidebar__logo" aria-hidden="true"></span><span class="cn-sidebar__brand-name">Lumen</span></div><div class="cn-sidebar__search cn-input" data-variant="default" data-size="md"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search" aria-label="Search"></div><div class="cn-sidebar__nav"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg><span class="cn-sidebar__label">Home</span></a><a href="#" class="cn-sidebar__item" aria-current="page"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><span class="cn-sidebar__label">Dashboard</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-sidebar__label">Projects</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-sidebar__label">Tasks</span><span class="cn-sidebar__badge">10</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-sidebar__label">Reporting</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><span class="cn-sidebar__label">Users</span></a></div><div class="cn-sidebar__footer"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg><span class="cn-sidebar__label">Support</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg><span class="cn-sidebar__label">Settings</span></a><div class="cn-sidebar__account"><span class="cn-sidebar__avatar" aria-hidden="true">MC</span><div class="cn-sidebar__account-text"><span class="cn-sidebar__name">Maya Chen</span><span class="cn-sidebar__email">maya@lumen.co</span></div><button type="button" class="cn-sidebar__account-action" aria-label="Log out"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button></div></div></nav><main class="cn-app__main"><header class="cn-page-header" data-variant="simple" data-divider="yes"><div class="cn-page-header__content"><h1 class="cn-page-header__title">Welcome back, Maya</h1><p class="cn-page-header__description">Track, manage and forecast your customers and orders.</p></div><div class="cn-page-header__actions"><button type="button" class="cn-button" data-variant="outline" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg><span class="cn-button__label">Export</span></button><button type="button" class="cn-button" data-variant="primary" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg><span class="cn-button__label">New report</span></button></div></header><div class="cn-grid" data-cols="4" data-gap="6" data-keep><div class="cn-stat" data-size="md" data-variant="card" data-trend="up"><div class="cn-stat__label">Total customers</div><div class="cn-stat__value">2,420</div><div class="cn-stat__delta"><svg class="cn-stat__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 13V3M4 7l4-4 4 4"/></svg><span>+40%</span></div><div class="cn-stat__description">vs last month</div></div><div class="cn-stat" data-size="md" data-variant="card" data-trend="up"><div class="cn-stat__label">Members</div><div class="cn-stat__value">1,210</div><div class="cn-stat__delta"><svg class="cn-stat__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 13V3M4 7l4-4 4 4"/></svg><span>+10%</span></div><div class="cn-stat__description">vs last month</div></div><div class="cn-stat" data-size="md" data-variant="card" data-trend="down"><div class="cn-stat__label">Active now</div><div class="cn-stat__value">316</div><div class="cn-stat__delta"><svg class="cn-stat__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 3v10M4 9l4 4 4-4"/></svg><span>−2%</span></div><div class="cn-stat__description">vs last month</div></div><div class="cn-stat" data-size="md" data-variant="card" data-trend="up"><div class="cn-stat__label">Conversion</div><div class="cn-stat__value">4.2%</div><div class="cn-stat__delta"><svg class="cn-stat__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 13V3M4 7l4-4 4 4"/></svg><span>+0.4%</span></div><div class="cn-stat__description">vs last month</div></div></div><div class="cn-grid" data-cols="3" data-gap="6"><div class="cn-app__span2"><section class="cn-card" data-variant="default" data-padding="none"><div class="cn-section-header" data-variant="card" data-divider="yes"><div class="cn-section-header__content"><h2 class="cn-section-header__title">Revenue</h2><p class="cn-section-header__description">Monthly recurring revenue, last 12 months</p></div><div class="cn-section-header__actions"><div class="cn-tabs" data-variant="pill" data-size="md" role="tablist"><button type="button" class="cn-tabs__tab" role="tab" aria-selected="true">12 months</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">30 days</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">7 days</button></div></div></div><div class="cn-card__body cn-app__card-body"><div class="cn-stack" data-gap="4"><div class="cn-row" data-justify="between"><span class="cn-text-heading-lg">$128,430</span><span class="cn-app__legend"><span>This year</span><span>Last year</span></span></div><svg class="cn-app__chart" viewBox="0 0 800 260" preserveAspectRatio="none" role="img" aria-label="Line chart placeholder"><g class="cn-app__grid"><line x1="0" x2="800" y1="52" y2="52"/><line x1="0" x2="800" y1="104" y2="104"/><line x1="0" x2="800" y1="156" y2="156"/><line x1="0" x2="800" y1="208" y2="208"/></g><path class="cn-app__area" d="M0 195 C100 182 150 130 240 143 S400 78 480 91 S640 39 800 52 V260 H0 Z"/><path class="cn-app__line" d="M0 195 C100 182 150 130 240 143 S400 78 480 91 S640 39 800 52"/></svg></div></div></section></div><section class="cn-card" data-variant="default" data-padding="none"><div class="cn-section-header" data-variant="card" data-divider="yes"><div class="cn-section-header__content"><h2 class="cn-section-header__title">Recent activity</h2></div><div class="cn-section-header__actions"><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">View all</span></button></div></div><div class="cn-card__body cn-app__card-body"><ul class="cn-list" data-density="default" data-variant="plain"><li class="cn-list__item"><span class="cn-list__leading"><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="accent" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span></span><span class="cn-list__content"><span class="cn-list__title">Maya Chen</span><span class="cn-list__description">commented on Q3 roadmap</span></span><span class="cn-list__trailing">2m</span></li><li class="cn-list__item"><span class="cn-list__leading"><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="success" data-status="none" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span></span></span><span class="cn-list__content"><span class="cn-list__title">Daniel Costa</span><span class="cn-list__description">merged “Billing refactor”</span></span><span class="cn-list__trailing">14m</span></li><li class="cn-list__item"><span class="cn-list__leading"><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="warning" data-status="none" role="img" aria-label="Sofia Almeida"><span class="cn-avatar__fallback" aria-hidden="true">SA</span></span></span><span class="cn-list__content"><span class="cn-list__title">Sofia Almeida</span><span class="cn-list__description">uploaded 3 files to Brand</span></span><span class="cn-list__trailing">1h</span></li><li class="cn-list__item"><span class="cn-list__leading"><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="info" data-status="none" role="img" aria-label="Lucas Ferreira"><span class="cn-avatar__fallback" aria-hidden="true">LF</span></span></span><span class="cn-list__content"><span class="cn-list__title">Lucas Ferreira</span><span class="cn-list__description">invited Elena Rossi</span></span><span class="cn-list__trailing">3h</span></li></ul></div></section></div><section class="cn-card cn-app__table-card" data-variant="default" data-padding="none"><div class="cn-section-header" data-variant="card" data-divider="yes"><div class="cn-section-header__content"><h2 class="cn-section-header__title">Team members <span class="cn-section-header__badge"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">100 users</span></span></h2><p class="cn-section-header__description">Manage your team members and their account permissions here.</p></div><div class="cn-section-header__actions"><button type="button" class="cn-button" data-variant="outline" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-button__label">Download all</span></button><button type="button" class="cn-button" data-variant="primary" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg><span class="cn-button__label">Add member</span></button></div></div><div class="cn-card__body cn-app__card-body cn-app__card-body--flush" role="region" tabindex="0" aria-label="Scrollable table"><table class="cn-table" data-density="default" data-variant="default" data-interactive><thead class="cn-table__head"><tr class="cn-table__row"><th class="cn-table__header-cell" scope="col" style="width:48px"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></th><th class="cn-table__header-cell" scope="col">Name</th><th class="cn-table__header-cell" scope="col">Status</th><th class="cn-table__header-cell" scope="col">Role</th><th class="cn-table__header-cell" scope="col">Email address</th><th class="cn-table__header-cell" scope="col">Teams</th><th class="cn-table__header-cell" scope="col"><span class="cn-sr-only">Actions</span></th></tr></thead><tbody class="cn-table__body"><tr class="cn-table__row"><td class="cn-table__cell"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-row" data-gap="3"><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="accent" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span><div class="cn-stack" data-gap="0"><span class="cn-text-label-sm">Maya Chen</span><span class="cn-text-body-sm cn-app__muted">@maya</span></div></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Active</span></td><td class="cn-table__cell">Product Manager</td><td class="cn-table__cell">maya@lumen.co</td><td class="cn-table__cell"><div class="cn-row" data-gap="1"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Design</span><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">Product</span></div></td><td class="cn-table__cell cn-table__actions"><div class="cn-row" data-gap="1" data-justify="end"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Delete</span></button><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Edit</span></button></div></td></tr><tr class="cn-table__row" data-selected><td class="cn-table__cell"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-row" data-gap="3"><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="success" data-status="none" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span></span><div class="cn-stack" data-gap="0"><span class="cn-text-label-sm">Daniel Costa</span><span class="cn-text-body-sm cn-app__muted">@daniel</span></div></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Active</span></td><td class="cn-table__cell">Engineering</td><td class="cn-table__cell">daniel@lumen.co</td><td class="cn-table__cell"><div class="cn-row" data-gap="1"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Design</span><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">Product</span><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">+2</span></div></td><td class="cn-table__cell cn-table__actions"><div class="cn-row" data-gap="1" data-justify="end"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Delete</span></button><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Edit</span></button></div></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-row" data-gap="3"><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="warning" data-status="none" role="img" aria-label="Sofia Almeida"><span class="cn-avatar__fallback" aria-hidden="true">SA</span></span><div class="cn-stack" data-gap="0"><span class="cn-text-label-sm">Sofia Almeida</span><span class="cn-text-body-sm cn-app__muted">@sofia</span></div></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Offline</span></td><td class="cn-table__cell">Design</td><td class="cn-table__cell">sofia@lumen.co</td><td class="cn-table__cell"><div class="cn-row" data-gap="1"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Design</span><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">Product</span></div></td><td class="cn-table__cell cn-table__actions"><div class="cn-row" data-gap="1" data-justify="end"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Delete</span></button><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Edit</span></button></div></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-row" data-gap="3"><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="info" data-status="none" role="img" aria-label="Lucas Ferreira"><span class="cn-avatar__fallback" aria-hidden="true">LF</span></span><div class="cn-stack" data-gap="0"><span class="cn-text-label-sm">Lucas Ferreira</span><span class="cn-text-body-sm cn-app__muted">@lucas</span></div></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Active</span></td><td class="cn-table__cell">Frontend</td><td class="cn-table__cell">lucas@lumen.co</td><td class="cn-table__cell"><div class="cn-row" data-gap="1"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Design</span><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">Product</span><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">+2</span></div></td><td class="cn-table__cell cn-table__actions"><div class="cn-row" data-gap="1" data-justify="end"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Delete</span></button><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Edit</span></button></div></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-row" data-gap="3"><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="accent" data-status="none" role="img" aria-label="Aisha Khan"><span class="cn-avatar__fallback" aria-hidden="true">AK</span></span><div class="cn-stack" data-gap="0"><span class="cn-text-label-sm">Aisha Khan</span><span class="cn-text-body-sm cn-app__muted">@aisha</span></div></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Active</span></td><td class="cn-table__cell">Backend</td><td class="cn-table__cell">aisha@lumen.co</td><td class="cn-table__cell"><div class="cn-row" data-gap="1"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Design</span><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">Product</span></div></td><td class="cn-table__cell cn-table__actions"><div class="cn-row" data-gap="1" data-justify="end"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Delete</span></button><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Edit</span></button></div></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-row" data-gap="3"><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="success" data-status="none" role="img" aria-label="Noah Berg"><span class="cn-avatar__fallback" aria-hidden="true">NB</span></span><div class="cn-stack" data-gap="0"><span class="cn-text-label-sm">Noah Berg</span><span class="cn-text-body-sm cn-app__muted">@noah</span></div></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Offline</span></td><td class="cn-table__cell">Product Design</td><td class="cn-table__cell">noah@lumen.co</td><td class="cn-table__cell"><div class="cn-row" data-gap="1"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Design</span><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">Product</span><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">+2</span></div></td><td class="cn-table__cell cn-table__actions"><div class="cn-row" data-gap="1" data-justify="end"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Delete</span></button><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Edit</span></button></div></td></tr></tbody></table></div><footer class="cn-card__footer"><div class="cn-row" data-justify="between" data-gap="3"><span class="cn-text-body-sm cn-app__muted">Page 1 of 10</span><div class="cn-row" data-gap="2"><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Previous</span></button><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Next</span></button></div></div></footer></section></main></div>
                                Layout guidelines
                                • Structure: .cn-app (flex) → Sidebar + .cn-app__main (32px padding, 32px gap between blocks). Use the Sidebar component’s current width and navigation density.
                                • Order: page header → metrics (4 stat cards, 24px gap) → chart + activity (2:1 grid) → table card with its own header and pagination footer.
                                • Exactly one primary button on the page (in the header); card headers use outline/link buttons.
                                • Charts live inside a card with a section header (title, description, period tabs) and a legend; no 3D, no gradients, brand color for the main series and brand-300 for the secondary.
                                • Below 1024px the sidebar hides (use the MobileHeader) and grids collapse: metrics 2×2, chart and activity stacked.
                                Application pages

                                Files and documents

                                A file manager: breadcrumbs, toolbar (search, filters, list/grid toggle), a selectable table of files with owner and size, or a grid of file cards with previews; an upload dropzone and a details drawer.

                                Product previewPrimary layout
                                1280 × 760

                                Q3 launch

                                6 files · 25.1 MB · shared with 8 people

                                NameTypeSizeOwnerModifiedActions
                                Brand guidelines v3.pdf
                                PDF4.2 MB
                                Sofia Almeida
                                Today
                                Q3 roadmap.fig
                                Figma18.6 MB
                                Maya Chen
                                Yesterday
                                Billing migration plan.docx
                                Document212 KB
                                Daniel Costa
                                Mon
                                Onboarding screens.png
                                Image1.8 MB
                                Noah Berg
                                Mon
                                Customer interviews.csv
                                Spreadsheet96 KB
                                Aisha Khan
                                Last week
                                Launch checklist.md
                                Markdown12 KB
                                Lucas Ferreira
                                Last week
                                Page 1 of 10
                                HTML
                                <div class="cn-app"><nav class="cn-sidebar" data-variant="simple" data-theme="light" aria-label="Main"><div class="cn-sidebar__brand"><span class="cn-sidebar__logo" aria-hidden="true"></span><span class="cn-sidebar__brand-name">Lumen</span></div><div class="cn-sidebar__search cn-input" data-variant="default" data-size="md"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search" aria-label="Search"></div><div class="cn-sidebar__nav"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg><span class="cn-sidebar__label">Home</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><span class="cn-sidebar__label">Dashboard</span></a><a href="#" class="cn-sidebar__item" aria-current="page"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-sidebar__label">Projects</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-sidebar__label">Tasks</span><span class="cn-sidebar__badge">10</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-sidebar__label">Reporting</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><span class="cn-sidebar__label">Users</span></a></div><div class="cn-sidebar__footer"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg><span class="cn-sidebar__label">Support</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg><span class="cn-sidebar__label">Settings</span></a><div class="cn-sidebar__account"><span class="cn-sidebar__avatar" aria-hidden="true">MC</span><div class="cn-sidebar__account-text"><span class="cn-sidebar__name">Maya Chen</span><span class="cn-sidebar__email">maya@lumen.co</span></div><button type="button" class="cn-sidebar__account-action" aria-label="Log out"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button></div></div></nav><main class="cn-app__main"><header class="cn-page-header" data-variant="simple" data-divider="yes"><div class="cn-page-header__crumbs"><nav class="cn-breadcrumb" data-size="md" aria-label="Breadcrumb"><ol class="cn-breadcrumb__list"><li class="cn-breadcrumb__item"><a class="cn-breadcrumb__link" href="#">Files</a></li><li class="cn-breadcrumb__separator" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></li><li class="cn-breadcrumb__item"><a class="cn-breadcrumb__link" href="#">Product</a></li><li class="cn-breadcrumb__separator" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></li><li class="cn-breadcrumb__item"><span class="cn-breadcrumb__current" aria-current="page">Q3 launch</span></li></ol></nav></div><div class="cn-page-header__content"><h1 class="cn-page-header__title">Q3 launch</h1><p class="cn-page-header__description">6 files · 25.1 MB · shared with 8 people</p></div><div class="cn-page-header__actions"><button type="button" class="cn-button" data-variant="outline" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><span class="cn-button__label">Share</span></button><button type="button" class="cn-button" data-variant="primary" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg><span class="cn-button__label">Upload</span></button></div></header><div class="cn-row" data-justify="between" data-gap="3" data-wrap><div class="cn-row" data-gap="3"><div class="cn-input" data-variant="default" data-size="md" style="width:320px"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search files" aria-label="Search files"></div><button type="button" class="cn-button" data-variant="outline" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg><span class="cn-button__label">Type</span></button><button type="button" class="cn-button" data-variant="outline" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg><span class="cn-button__label">Owner</span></button></div><div class="cn-segmented-control" data-size="md" data-variant="default" role="radiogroup" aria-label="View"><button type="button" class="cn-segmented-control__item" role="radio" aria-checked="true">List</button><button type="button" class="cn-segmented-control__item" role="radio" aria-checked="false">Grid</button></div></div><section class="cn-card cn-app__table-card" data-variant="default" data-padding="none"><div class="cn-card__body cn-app__card-body cn-app__card-body--flush" role="region" tabindex="0" aria-label="Scrollable table"><table class="cn-table" data-density="default" data-variant="default" data-interactive><thead class="cn-table__head"><tr class="cn-table__row"><th class="cn-table__header-cell" scope="col" style="width:48px"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></th><th class="cn-table__header-cell" scope="col">Name</th><th class="cn-table__header-cell" scope="col">Type</th><th class="cn-table__header-cell" scope="col" data-align="right">Size</th><th class="cn-table__header-cell" scope="col">Owner</th><th class="cn-table__header-cell" scope="col">Modified</th><th class="cn-table__header-cell" scope="col"><span class="cn-sr-only">Actions</span></th></tr></thead><tbody class="cn-table__body"><tr class="cn-table__row"><td class="cn-table__cell"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-row" data-gap="3"><span class="cn-files__icon" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg></span><span class="cn-text-label-sm">Brand guidelines v3.pdf</span></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">PDF</span></td><td class="cn-table__cell" data-align="right">4.2 MB</td><td class="cn-table__cell"><div class="cn-row" data-gap="2"><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="warning" data-status="none" role="img" aria-label="Sofia Almeida"><span class="cn-avatar__fallback" aria-hidden="true">SA</span></span><span>Sofia Almeida</span></div></td><td class="cn-table__cell">Today</td><td class="cn-table__cell cn-table__actions"><div class="cn-row" data-gap="1" data-justify="end"><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="Download"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="More"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.25"/><circle cx="8" cy="8" r="1.25"/><circle cx="12.5" cy="8" r="1.25"/></svg></button></div></td></tr><tr class="cn-table__row" data-selected><td class="cn-table__cell"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-row" data-gap="3"><span class="cn-files__icon" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg></span><span class="cn-text-label-sm">Q3 roadmap.fig</span></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">Figma</span></td><td class="cn-table__cell" data-align="right">18.6 MB</td><td class="cn-table__cell"><div class="cn-row" data-gap="2"><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="accent" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span><span>Maya Chen</span></div></td><td class="cn-table__cell">Yesterday</td><td class="cn-table__cell cn-table__actions"><div class="cn-row" data-gap="1" data-justify="end"><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="Download"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="More"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.25"/><circle cx="8" cy="8" r="1.25"/><circle cx="12.5" cy="8" r="1.25"/></svg></button></div></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-row" data-gap="3"><span class="cn-files__icon" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg></span><span class="cn-text-label-sm">Billing migration plan.docx</span></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">Document</span></td><td class="cn-table__cell" data-align="right">212 KB</td><td class="cn-table__cell"><div class="cn-row" data-gap="2"><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="success" data-status="none" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span></span><span>Daniel Costa</span></div></td><td class="cn-table__cell">Mon</td><td class="cn-table__cell cn-table__actions"><div class="cn-row" data-gap="1" data-justify="end"><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="Download"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="More"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.25"/><circle cx="8" cy="8" r="1.25"/><circle cx="12.5" cy="8" r="1.25"/></svg></button></div></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-row" data-gap="3"><span class="cn-files__icon" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg></span><span class="cn-text-label-sm">Onboarding screens.png</span></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">Image</span></td><td class="cn-table__cell" data-align="right">1.8 MB</td><td class="cn-table__cell"><div class="cn-row" data-gap="2"><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="success" data-status="none" role="img" aria-label="Noah Berg"><span class="cn-avatar__fallback" aria-hidden="true">NB</span></span><span>Noah Berg</span></div></td><td class="cn-table__cell">Mon</td><td class="cn-table__cell cn-table__actions"><div class="cn-row" data-gap="1" data-justify="end"><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="Download"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="More"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.25"/><circle cx="8" cy="8" r="1.25"/><circle cx="12.5" cy="8" r="1.25"/></svg></button></div></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-row" data-gap="3"><span class="cn-files__icon" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg></span><span class="cn-text-label-sm">Customer interviews.csv</span></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">Spreadsheet</span></td><td class="cn-table__cell" data-align="right">96 KB</td><td class="cn-table__cell"><div class="cn-row" data-gap="2"><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="accent" data-status="none" role="img" aria-label="Aisha Khan"><span class="cn-avatar__fallback" aria-hidden="true">AK</span></span><span>Aisha Khan</span></div></td><td class="cn-table__cell">Last week</td><td class="cn-table__cell cn-table__actions"><div class="cn-row" data-gap="1" data-justify="end"><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="Download"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="More"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.25"/><circle cx="8" cy="8" r="1.25"/><circle cx="12.5" cy="8" r="1.25"/></svg></button></div></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-row" data-gap="3"><span class="cn-files__icon" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg></span><span class="cn-text-label-sm">Launch checklist.md</span></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">Markdown</span></td><td class="cn-table__cell" data-align="right">12 KB</td><td class="cn-table__cell"><div class="cn-row" data-gap="2"><span class="cn-avatar" data-size="xs" data-shape="circle" data-tone="info" data-status="none" role="img" aria-label="Lucas Ferreira"><span class="cn-avatar__fallback" aria-hidden="true">LF</span></span><span>Lucas Ferreira</span></div></td><td class="cn-table__cell">Last week</td><td class="cn-table__cell cn-table__actions"><div class="cn-row" data-gap="1" data-justify="end"><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="Download"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="More"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.25"/><circle cx="8" cy="8" r="1.25"/><circle cx="12.5" cy="8" r="1.25"/></svg></button></div></td></tr></tbody></table></div><footer class="cn-card__footer"><div class="cn-row" data-justify="between" data-gap="3"><span class="cn-text-body-sm cn-app__muted">Page 1 of 10</span><div class="cn-row" data-gap="2"><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Previous</span></button><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Next</span></button></div></div></footer></section></main></div>
                                Layout guidelines
                                • Breadcrumbs above the page title show the folder path (max 3 levels shown, the rest collapsed).
                                • Toolbar: search (320) + filter buttons on the left, list/grid segmented control on the right.
                                • List: table with checkbox, icon + name (label-sm), type badge, size right-aligned, owner (avatar xs + name), modified, and ghost icon actions. Selected rows get the action-subtle fill.
                                • Grid: cards with a 16:10 preview, name, type badge and size; 4 per row, 24px gap.
                                • Upload: a dropzone card at the top when the folder is empty or on hover of the page in real apps; never a floating "+" button.
                                Application pages

                                First run and empty states

                                What the app looks like before there is data: a dashboard with a setup checklist and empty widgets, an empty table with an import call to action, a no-results state and an error state.

                                Product previewPrimary layout
                                1280 × 760

                                Welcome to Lumen, Maya

                                Let’s get your workspace ready. You can come back to this any time.

                                Get started with Lumen

                                2 of 5 steps done · about 8 minutes left

                                1. Create your workspaceDone on 11 September
                                2. Invite your team2 people joined
                                3. Import your customersCSV or connect your CRM in one click.
                                4. Connect SlackGet notified where your team already talks.
                                5. Create your first reportPick a template to see your data in minutes.

                                Revenue

                                No revenue yet

                                Connect your billing provider to see revenue here.

                                Customers

                                No customers yet

                                Import a CSV or add your first customer.

                                Activity

                                Nothing happened yet

                                Activity from your team will show up here.

                                HTML
                                <div class="cn-app"><nav class="cn-sidebar" data-variant="simple" data-theme="light" aria-label="Main"><div class="cn-sidebar__brand"><span class="cn-sidebar__logo" aria-hidden="true"></span><span class="cn-sidebar__brand-name">Lumen</span></div><div class="cn-sidebar__search cn-input" data-variant="default" data-size="md"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search" aria-label="Search"></div><div class="cn-sidebar__nav"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg><span class="cn-sidebar__label">Home</span></a><a href="#" class="cn-sidebar__item" aria-current="page"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><span class="cn-sidebar__label">Dashboard</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-sidebar__label">Projects</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-sidebar__label">Tasks</span><span class="cn-sidebar__badge">10</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-sidebar__label">Reporting</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><span class="cn-sidebar__label">Users</span></a></div><div class="cn-sidebar__footer"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg><span class="cn-sidebar__label">Support</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg><span class="cn-sidebar__label">Settings</span></a><div class="cn-sidebar__account"><span class="cn-sidebar__avatar" aria-hidden="true">MC</span><div class="cn-sidebar__account-text"><span class="cn-sidebar__name">Maya Chen</span><span class="cn-sidebar__email">maya@lumen.co</span></div><button type="button" class="cn-sidebar__account-action" aria-label="Log out"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button></div></div></nav><main class="cn-app__main"><header class="cn-page-header" data-variant="simple" data-divider="yes"><div class="cn-page-header__content"><h1 class="cn-page-header__title">Welcome to Lumen, Maya</h1><p class="cn-page-header__description">Let’s get your workspace ready. You can come back to this any time.</p></div><div class="cn-page-header__actions"><button type="button" class="cn-button" data-variant="primary" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg><span class="cn-button__label">Import customers</span></button></div></header><section class="cn-card" data-variant="default" data-padding="none"><div class="cn-section-header" data-variant="card" data-divider="yes"><div class="cn-section-header__content"><h2 class="cn-section-header__title">Get started with Lumen</h2><p class="cn-section-header__description">2 of 5 steps done · about 8 minutes left</p></div><div class="cn-section-header__actions"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Dismiss</span></button></div></div><div class="cn-card__body cn-app__card-body cn-app__card-body--flush"><div class="cn-progress" data-size="sm" data-tone="action" role="progressbar" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" aria-label="Setup progress" style="padding:var(--cn-space-4) var(--cn-space-6) 0"><div class="cn-progress__track"><div class="cn-progress__indicator" style="width:40%"></div></div></div><ol class="cn-first-run__list"><li class="cn-first-run__step" data-done="true"><span class="cn-first-run__check" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-stack cn-flex-1" data-gap="0"><span class="cn-text-label-sm cn-first-run__done">Create your workspace</span><span class="cn-text-body-sm cn-app__muted">Done on 11 September</span></span></li><li class="cn-first-run__step" data-done="true"><span class="cn-first-run__check" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-stack cn-flex-1" data-gap="0"><span class="cn-text-label-sm cn-first-run__done">Invite your team</span><span class="cn-text-body-sm cn-app__muted">2 people joined</span></span></li><li class="cn-first-run__step" data-done="false"><span class="cn-first-run__check" aria-hidden="true"></span><span class="cn-stack cn-flex-1" data-gap="0"><span class="cn-text-label-sm">Import your customers</span><span class="cn-text-body-sm cn-app__muted">CSV or connect your CRM in one click.</span></span><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Import</span></button></li><li class="cn-first-run__step" data-done="false"><span class="cn-first-run__check" aria-hidden="true"></span><span class="cn-stack cn-flex-1" data-gap="0"><span class="cn-text-label-sm">Connect Slack</span><span class="cn-text-body-sm cn-app__muted">Get notified where your team already talks.</span></span><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Connect</span></button></li><li class="cn-first-run__step" data-done="false"><span class="cn-first-run__check" aria-hidden="true"></span><span class="cn-stack cn-flex-1" data-gap="0"><span class="cn-text-label-sm">Create your first report</span><span class="cn-text-body-sm cn-app__muted">Pick a template to see your data in minutes.</span></span><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Create</span></button></li></ol></div></section><div class="cn-grid" data-cols="3" data-gap="6"><section class="cn-card" data-variant="default" data-padding="none"><div class="cn-section-header" data-variant="card" data-divider="yes"><div class="cn-section-header__content"><h2 class="cn-section-header__title">Revenue</h2></div></div><div class="cn-card__body cn-app__card-body"><div class="cn-empty-state" data-size="md" data-variant="default"><svg class="cn-empty-state__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M2.5 13.5h11M4.5 11V7M8 11V4M11.5 11V8.5"/></svg><h3 class="cn-empty-state__title">No revenue yet</h3><p class="cn-empty-state__description">Connect your billing provider to see revenue here.</p><div class="cn-empty-state__actions"><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Connect billing</span></button></div></div></div></section><section class="cn-card" data-variant="default" data-padding="none"><div class="cn-section-header" data-variant="card" data-divider="yes"><div class="cn-section-header__content"><h2 class="cn-section-header__title">Customers</h2></div></div><div class="cn-card__body cn-app__card-body"><div class="cn-empty-state" data-size="md" data-variant="default"><svg class="cn-empty-state__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><h3 class="cn-empty-state__title">No customers yet</h3><p class="cn-empty-state__description">Import a CSV or add your first customer.</p><div class="cn-empty-state__actions"><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Import</span></button></div></div></div></section><section class="cn-card" data-variant="default" data-padding="none"><div class="cn-section-header" data-variant="card" data-divider="yes"><div class="cn-section-header__content"><h2 class="cn-section-header__title">Activity</h2></div></div><div class="cn-card__body cn-app__card-body"><div class="cn-empty-state" data-size="md" data-variant="default"><svg class="cn-empty-state__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><h3 class="cn-empty-state__title">Nothing happened yet</h3><p class="cn-empty-state__description">Activity from your team will show up here.</p><div class="cn-empty-state__actions"></div></div></div></section></div></main></div>
                                Layout guidelines
                                • A setup checklist card replaces the metrics on the first visit: title, progress ("2 of 5 steps"), a progress bar and steps with a check circle, title, one-line description and an outline action; done steps are struck through and lose their button.
                                • Empty widgets keep their card and header so the layout does not jump when data arrives; the body shows a small EmptyState (icon, one line, one link button).
                                • Empty table: keep the page header, filters and the card; the body shows an EmptyState with the primary action ("Import customers") and a secondary ("Add manually").
                                • No results: keep the search term visible in the description and offer "Clear search" (outline) before any primary action.
                                • Error state: featured icon error outline, plain language ("We couldn’t load your customers"), a retry button and a support link; never a stack trace.
                                Application pages

                                Inbox and messaging

                                Three-pane inbox (sidebar, thread list, reading pane with composer), a chat conversation layout, and a notification center. The list stays visible while reading.

                                Product previewPrimary layout
                                1280 × 760

                                Inbox 24

                                Q3 roadmap review

                                ProductNeeds reply
                                Maya ChenToday, 9:41

                                Hi Daniel,

                                Can we move the roadmap review to Thursday? I want to include the billing numbers from the new dashboard and they only close on Wednesday night.

                                If Thursday doesn’t work, Friday morning is also fine for me.

                                Thanks,
                                Maya

                                Daniel CostaToday, 10:02

                                Thursday works. I’ll update the invite and add Sofia so the brand assets are covered in the same session.

                                HTML
                                <div class="cn-app"><nav class="cn-sidebar" data-variant="simple" data-theme="light" aria-label="Main"><div class="cn-sidebar__brand"><span class="cn-sidebar__logo" aria-hidden="true"></span><span class="cn-sidebar__brand-name">Lumen</span></div><div class="cn-sidebar__search cn-input" data-variant="default" data-size="md"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search" aria-label="Search"></div><div class="cn-sidebar__nav"><a href="#" class="cn-sidebar__item" aria-current="page"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg><span class="cn-sidebar__label">Home</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><span class="cn-sidebar__label">Dashboard</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-sidebar__label">Projects</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-sidebar__label">Tasks</span><span class="cn-sidebar__badge">10</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-sidebar__label">Reporting</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><span class="cn-sidebar__label">Users</span></a></div><div class="cn-sidebar__footer"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg><span class="cn-sidebar__label">Support</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg><span class="cn-sidebar__label">Settings</span></a><div class="cn-sidebar__account"><span class="cn-sidebar__avatar" aria-hidden="true">MC</span><div class="cn-sidebar__account-text"><span class="cn-sidebar__name">Maya Chen</span><span class="cn-sidebar__email">maya@lumen.co</span></div><button type="button" class="cn-sidebar__account-action" aria-label="Log out"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button></div></div></nav><section class="cn-inbox__list"><div class="cn-inbox__list-head"><div class="cn-row" data-justify="between"><h2 class="cn-text-heading-xs">Inbox <span class="cn-text-body-sm cn-app__muted">24</span></h2><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="Compose"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg></button></div><div class="cn-input" data-variant="default" data-size="sm"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search messages" aria-label="Search messages"></div><div class="cn-tabs" data-variant="pill" data-size="md" role="tablist"><button type="button" class="cn-tabs__tab" role="tab" aria-selected="true">All</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">Unread</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">Flagged</button></div></div><div class="cn-inbox__threads"><a href="#" class="cn-inbox__thread cn-inbox__thread--active" data-unread><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="accent" data-status="online" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span><span class="cn-avatar__status"></span></span><span class="cn-inbox__thread-body"><span class="cn-row" data-justify="between" data-gap="2"><span class="cn-text-label-sm cn-inbox__name">Maya Chen</span><span class="cn-text-body-xs cn-app__muted">9:41</span></span><span class="cn-text-label-sm cn-inbox__subject">Q3 roadmap review</span><span class="cn-text-body-sm cn-app__muted cn-inbox__preview">Can we move the review to Thursday? I want to include the billing numbers…</span></span></a><a href="#" class="cn-inbox__thread"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="success" data-status="online" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span><span class="cn-avatar__status"></span></span><span class="cn-inbox__thread-body"><span class="cn-row" data-justify="between" data-gap="2"><span class="cn-text-label-sm cn-inbox__name">Daniel Costa</span><span class="cn-text-body-xs cn-app__muted">8:20</span></span><span class="cn-text-label-sm cn-inbox__subject">Billing refactor merged</span><span class="cn-text-body-sm cn-app__muted cn-inbox__preview">PR #482 is in. Migrations run on deploy; nothing to do on your side.</span></span></a><a href="#" class="cn-inbox__thread"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="warning" data-status="online" role="img" aria-label="Sofia Almeida"><span class="cn-avatar__fallback" aria-hidden="true">SA</span><span class="cn-avatar__status"></span></span><span class="cn-inbox__thread-body"><span class="cn-row" data-justify="between" data-gap="2"><span class="cn-text-label-sm cn-inbox__name">Sofia Almeida</span><span class="cn-text-body-xs cn-app__muted">Yesterday</span></span><span class="cn-text-label-sm cn-inbox__subject">Brand assets v3</span><span class="cn-text-body-sm cn-app__muted cn-inbox__preview">Uploaded the new logo set and the updated icon grid to the Brand folder.</span></span></a><a href="#" class="cn-inbox__thread" data-unread><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="info" data-status="online" role="img" aria-label="Lucas Ferreira"><span class="cn-avatar__fallback" aria-hidden="true">LF</span><span class="cn-avatar__status"></span></span><span class="cn-inbox__thread-body"><span class="cn-row" data-justify="between" data-gap="2"><span class="cn-text-label-sm cn-inbox__name">Lucas Ferreira</span><span class="cn-text-body-xs cn-app__muted">Yesterday</span></span><span class="cn-text-label-sm cn-inbox__subject">Drawer focus trap</span><span class="cn-text-body-sm cn-app__muted cn-inbox__preview">Found the bug: focus escapes the drawer when tabbing from the last button…</span></span></a><a href="#" class="cn-inbox__thread"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="accent" data-status="online" role="img" aria-label="Aisha Khan"><span class="cn-avatar__fallback" aria-hidden="true">AK</span><span class="cn-avatar__status"></span></span><span class="cn-inbox__thread-body"><span class="cn-row" data-justify="between" data-gap="2"><span class="cn-text-label-sm cn-inbox__name">Aisha Khan</span><span class="cn-text-body-xs cn-app__muted">Mon</span></span><span class="cn-text-label-sm cn-inbox__subject">Webhook retries</span><span class="cn-text-body-sm cn-app__muted cn-inbox__preview">Retries now back off exponentially; alert threshold set to 5 failures.</span></span></a><a href="#" class="cn-inbox__thread"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="success" data-status="online" role="img" aria-label="Noah Berg"><span class="cn-avatar__fallback" aria-hidden="true">NB</span><span class="cn-avatar__status"></span></span><span class="cn-inbox__thread-body"><span class="cn-row" data-justify="between" data-gap="2"><span class="cn-text-label-sm cn-inbox__name">Noah Berg</span><span class="cn-text-body-xs cn-app__muted">Mon</span></span><span class="cn-text-label-sm cn-inbox__subject">Onboarding copy</span><span class="cn-text-body-sm cn-app__muted cn-inbox__preview">Draft of the three onboarding screens attached. Tone check?</span></span></a></div></section><section class="cn-inbox__reading"><header class="cn-inbox__reading-head"><div class="cn-stack" data-gap="1"><h2 class="cn-text-heading-sm">Q3 roadmap review</h2><div class="cn-row" data-gap="2"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Product</span><span class="cn-badge" data-tone="warning" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Needs reply</span></div></div><div class="cn-row" data-gap="1"><button type="button" class="cn-icon-button" data-variant="ghost" data-size="md" data-shape="square" aria-label="Archive"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg></button><button type="button" class="cn-icon-button" data-variant="ghost" data-size="md" data-shape="square" aria-label="Delete"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4.5h10M6.5 4.5V3h3v1.5M4.5 4.5l.6 8.5h5.8l.6-8.5"/></svg></button><button type="button" class="cn-icon-button" data-variant="ghost" data-size="md" data-shape="square" aria-label="More"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.25"/><circle cx="8" cy="8" r="1.25"/><circle cx="12.5" cy="8" r="1.25"/></svg></button></div></header><div class="cn-inbox__messages"><article class="cn-inbox__msg"><header class="cn-row" data-gap="3"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="accent" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span><div class="cn-stack" data-gap="0"><span class="cn-text-label-sm">Maya Chen</span><span class="cn-text-body-xs cn-app__muted">Today, 9:41</span></div></header><div class="cn-inbox__msg-body cn-text-body-md"><p>Hi Daniel,</p><p>Can we move the roadmap review to Thursday? I want to include the billing numbers from the new dashboard and they only close on Wednesday night.</p><p>If Thursday doesn’t work, Friday morning is also fine for me.</p><p>Thanks,<br>Maya</p></div></article><article class="cn-inbox__msg"><header class="cn-row" data-gap="3"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="success" data-status="none" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span></span><div class="cn-stack" data-gap="0"><span class="cn-text-label-sm">Daniel Costa</span><span class="cn-text-body-xs cn-app__muted">Today, 10:02</span></div></header><div class="cn-inbox__msg-body cn-text-body-md"><p>Thursday works. I’ll update the invite and add Sofia so the brand assets are covered in the same session.</p></div></article></div><footer class="cn-inbox__composer"><div class="cn-textarea" data-size="md" data-resize="none"><textarea class="cn-textarea__field" rows="3" placeholder="Reply to Maya…" aria-label="Reply"></textarea></div><div class="cn-row" data-justify="between"><div class="cn-row" data-gap="1"><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="Attach file"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg></button><button type="button" class="cn-icon-button" data-variant="ghost" data-size="sm" data-shape="square" aria-label="Insert link"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button></div><div class="cn-row" data-gap="2"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Save draft</span></button><button type="button" class="cn-button" data-variant="primary" data-size="sm"><span class="cn-button__label">Send</span></button></div></div></footer></section></div>
                                Layout guidelines
                                • Three panes: navigation sidebar (296), thread list (360, own scroll, search + filter tabs at the top), reading pane (flex 1) with a header (subject, badges, actions), the messages and a sticky composer.
                                • Thread row: avatar 40 with presence, name label-sm + time body-xs on one line, subject label-sm, one-line preview body-sm muted; unread rows show a brand dot and semibold name; the open thread has the subtle fill.
                                • Messages are separated by 24px; each has the sender row (avatar 40, name, time) and the body in body-md with 16px paragraph spacing.
                                • Composer: textarea (3 rows, no resize) + toolbar of ghost icon buttons on the left and Save draft / Send on the right; Send is the only primary button on the page.
                                • Chat variant: bubbles max 70% wide, incoming on the left in bg-subtle, outgoing on the right in the action color; a date divider chip between days.
                                • Notification center: a list grouped by day, each item with avatar, actor + action text, time, and an unread dot; "Mark all as read" as a link button in the header.
                                Application pages

                                List with detail drawer

                                The workhorse app page: a filterable table of records with a detail drawer that opens over the list instead of navigating away. Also as a card list and as a kanban board.

                                Product previewPrimary layout
                                1280 × 760

                                Users

                                Manage the people in your workspace and their permissions.

                                All users 240 users

                                NameStatusRoleEmail addressTeamsActions
                                Maya Chen@maya
                                ActiveProduct Managermaya@lumen.co
                                DesignProduct
                                Daniel Costa@daniel
                                ActiveEngineeringdaniel@lumen.co
                                DesignProduct+2
                                Sofia Almeida@sofia
                                OfflineDesignsofia@lumen.co
                                DesignProduct
                                Lucas Ferreira@lucas
                                ActiveFrontendlucas@lumen.co
                                DesignProduct+2
                                Aisha Khan@aisha
                                ActiveBackendaisha@lumen.co
                                DesignProduct
                                Noah Berg@noah
                                OfflineProduct Designnoah@lumen.co
                                DesignProduct+2
                                Page 1 of 10
                                HTML
                                <div class="cn-app"><nav class="cn-sidebar" data-variant="simple" data-theme="light" aria-label="Main"><div class="cn-sidebar__brand"><span class="cn-sidebar__logo" aria-hidden="true"></span><span class="cn-sidebar__brand-name">Lumen</span></div><div class="cn-sidebar__search cn-input" data-variant="default" data-size="md"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search" aria-label="Search"></div><div class="cn-sidebar__nav"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg><span class="cn-sidebar__label">Home</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><span class="cn-sidebar__label">Dashboard</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-sidebar__label">Projects</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-sidebar__label">Tasks</span><span class="cn-sidebar__badge">10</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-sidebar__label">Reporting</span></a><a href="#" class="cn-sidebar__item" aria-current="page"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><span class="cn-sidebar__label">Users</span></a></div><div class="cn-sidebar__footer"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg><span class="cn-sidebar__label">Support</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg><span class="cn-sidebar__label">Settings</span></a><div class="cn-sidebar__account"><span class="cn-sidebar__avatar" aria-hidden="true">MC</span><div class="cn-sidebar__account-text"><span class="cn-sidebar__name">Maya Chen</span><span class="cn-sidebar__email">maya@lumen.co</span></div><button type="button" class="cn-sidebar__account-action" aria-label="Log out"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button></div></div></nav><main class="cn-app__main"><header class="cn-page-header" data-variant="simple" data-divider="yes"><div class="cn-page-header__content"><h1 class="cn-page-header__title">Users</h1><p class="cn-page-header__description">Manage the people in your workspace and their permissions.</p></div><div class="cn-page-header__actions"><button type="button" class="cn-button" data-variant="outline" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-button__label">Import</span></button><button type="button" class="cn-button" data-variant="primary" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg><span class="cn-button__label">Invite user</span></button></div></header><div class="cn-row" data-justify="between" data-gap="3" data-wrap><div class="cn-row" data-gap="3"><div class="cn-tabs" data-variant="pill" data-size="md" role="tablist"><button type="button" class="cn-tabs__tab" role="tab" aria-selected="true">View all</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">Active</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">Invited</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">Offline</button></div></div><div class="cn-row" data-gap="3"><div class="cn-input" data-variant="default" data-size="md" style="width:320px"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search" aria-label="Search members"></div><button type="button" class="cn-button" data-variant="outline" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M3 5h10M3 8h10M3 11h10"/></svg><span class="cn-button__label">Filters</span></button></div></div><section class="cn-card cn-app__table-card" data-variant="default" data-padding="none"><div class="cn-section-header" data-variant="card" data-divider="yes"><div class="cn-section-header__content"><h2 class="cn-section-header__title">All users <span class="cn-section-header__badge"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">240 users</span></span></h2></div></div><div class="cn-card__body cn-app__card-body cn-app__card-body--flush" role="region" tabindex="0" aria-label="Scrollable table"><table class="cn-table" data-density="default" data-variant="default" data-interactive><thead class="cn-table__head"><tr class="cn-table__row"><th class="cn-table__header-cell" scope="col" style="width:48px"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></th><th class="cn-table__header-cell" scope="col">Name</th><th class="cn-table__header-cell" scope="col">Status</th><th class="cn-table__header-cell" scope="col">Role</th><th class="cn-table__header-cell" scope="col">Email address</th><th class="cn-table__header-cell" scope="col">Teams</th><th class="cn-table__header-cell" scope="col"><span class="cn-sr-only">Actions</span></th></tr></thead><tbody class="cn-table__body"><tr class="cn-table__row"><td class="cn-table__cell"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-row" data-gap="3"><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="accent" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span><div class="cn-stack" data-gap="0"><span class="cn-text-label-sm">Maya Chen</span><span class="cn-text-body-sm cn-app__muted">@maya</span></div></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Active</span></td><td class="cn-table__cell">Product Manager</td><td class="cn-table__cell">maya@lumen.co</td><td class="cn-table__cell"><div class="cn-row" data-gap="1"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Design</span><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">Product</span></div></td><td class="cn-table__cell cn-table__actions"><div class="cn-row" data-gap="1" data-justify="end"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Delete</span></button><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Edit</span></button></div></td></tr><tr class="cn-table__row" data-selected><td class="cn-table__cell"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-row" data-gap="3"><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="success" data-status="none" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span></span><div class="cn-stack" data-gap="0"><span class="cn-text-label-sm">Daniel Costa</span><span class="cn-text-body-sm cn-app__muted">@daniel</span></div></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Active</span></td><td class="cn-table__cell">Engineering</td><td class="cn-table__cell">daniel@lumen.co</td><td class="cn-table__cell"><div class="cn-row" data-gap="1"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Design</span><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">Product</span><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">+2</span></div></td><td class="cn-table__cell cn-table__actions"><div class="cn-row" data-gap="1" data-justify="end"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Delete</span></button><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Edit</span></button></div></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-row" data-gap="3"><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="warning" data-status="none" role="img" aria-label="Sofia Almeida"><span class="cn-avatar__fallback" aria-hidden="true">SA</span></span><div class="cn-stack" data-gap="0"><span class="cn-text-label-sm">Sofia Almeida</span><span class="cn-text-body-sm cn-app__muted">@sofia</span></div></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Offline</span></td><td class="cn-table__cell">Design</td><td class="cn-table__cell">sofia@lumen.co</td><td class="cn-table__cell"><div class="cn-row" data-gap="1"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Design</span><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">Product</span></div></td><td class="cn-table__cell cn-table__actions"><div class="cn-row" data-gap="1" data-justify="end"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Delete</span></button><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Edit</span></button></div></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-row" data-gap="3"><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="info" data-status="none" role="img" aria-label="Lucas Ferreira"><span class="cn-avatar__fallback" aria-hidden="true">LF</span></span><div class="cn-stack" data-gap="0"><span class="cn-text-label-sm">Lucas Ferreira</span><span class="cn-text-body-sm cn-app__muted">@lucas</span></div></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Active</span></td><td class="cn-table__cell">Frontend</td><td class="cn-table__cell">lucas@lumen.co</td><td class="cn-table__cell"><div class="cn-row" data-gap="1"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Design</span><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">Product</span><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">+2</span></div></td><td class="cn-table__cell cn-table__actions"><div class="cn-row" data-gap="1" data-justify="end"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Delete</span></button><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Edit</span></button></div></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-row" data-gap="3"><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="accent" data-status="none" role="img" aria-label="Aisha Khan"><span class="cn-avatar__fallback" aria-hidden="true">AK</span></span><div class="cn-stack" data-gap="0"><span class="cn-text-label-sm">Aisha Khan</span><span class="cn-text-body-sm cn-app__muted">@aisha</span></div></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Active</span></td><td class="cn-table__cell">Backend</td><td class="cn-table__cell">aisha@lumen.co</td><td class="cn-table__cell"><div class="cn-row" data-gap="1"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Design</span><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">Product</span></div></td><td class="cn-table__cell cn-table__actions"><div class="cn-row" data-gap="1" data-justify="end"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Delete</span></button><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Edit</span></button></div></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Select row"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></label></td><td class="cn-table__cell"><div class="cn-row" data-gap="3"><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="success" data-status="none" role="img" aria-label="Noah Berg"><span class="cn-avatar__fallback" aria-hidden="true">NB</span></span><div class="cn-stack" data-gap="0"><span class="cn-text-label-sm">Noah Berg</span><span class="cn-text-body-sm cn-app__muted">@noah</span></div></div></td><td class="cn-table__cell"><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Offline</span></td><td class="cn-table__cell">Product Design</td><td class="cn-table__cell">noah@lumen.co</td><td class="cn-table__cell"><div class="cn-row" data-gap="1"><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Design</span><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">Product</span><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">+2</span></div></td><td class="cn-table__cell cn-table__actions"><div class="cn-row" data-gap="1" data-justify="end"><button type="button" class="cn-button" data-variant="ghost" data-size="sm"><span class="cn-button__label">Delete</span></button><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Edit</span></button></div></td></tr></tbody></table></div><footer class="cn-card__footer"><div class="cn-row" data-justify="between" data-gap="3"><span class="cn-text-body-sm cn-app__muted">Page 1 of 10</span><div class="cn-row" data-gap="2"><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Previous</span></button><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Next</span></button></div></div></footer></section></main><aside class="cn-app__drawer" aria-label="Member details"><div class="cn-app__drawer-head"><div class="cn-row" data-gap="4"><span class="cn-avatar" data-size="xl" data-shape="circle" data-tone="success" data-status="online" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span><span class="cn-avatar__status"></span></span><div class="cn-stack" data-gap="1"><span class="cn-text-heading-sm">Daniel Costa</span><span class="cn-text-body-md cn-app__muted">daniel@lumen.co</span><div class="cn-row" data-gap="1"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>Active</span><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">Engineering</span></div></div></div><button type="button" class="cn-close-button" data-size="sm" data-theme="light" aria-label="Close"><svg class="cn-close-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div><div class="cn-app__drawer-body"><div class="cn-tabs" data-variant="underline" data-size="md" role="tablist"><button type="button" class="cn-tabs__tab" role="tab" aria-selected="true">Details</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">Activity</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">Permissions</button></div><dl class="cn-app__dl"><div><dt>Role</dt><dd>Engineering Manager</dd></div><div><dt>Teams</dt><dd>Platform, Billing, Design systems</dd></div><div><dt>Location</dt><dd>Lisbon, Portugal · GMT+1</dd></div><div><dt>Joined</dt><dd>12 March 2024</dd></div><div><dt>Last active</dt><dd>2 minutes ago</dd></div></dl><div class="cn-alert" data-tone="warning" data-variant="soft" data-layout="inline" role="status"><span class="cn-alert__icon" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 2.5l6 11H2l6-11zM8 7v3M8 11.5v.5"/></svg></span><div class="cn-alert__content"><div class="cn-alert__title">Two-factor authentication is off</div><p class="cn-alert__description">Ask Daniel to enable it in Settings → Security.</p></div></div></div><div class="cn-app__drawer-foot"><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Remove from team</span></button><button type="button" class="cn-button" data-variant="primary" data-size="md"><span class="cn-button__label">Edit member</span></button></div></aside></div>
                                Layout guidelines
                                • Filters row above the table: view tabs (pill) on the left, search (320px) and a Filters button on the right.
                                • The table lives in a card with its header (title + count badge + actions) and a pagination footer.
                                • Selecting a row opens the 440px drawer on the right: header with avatar/title/status and a close button, tabs, a definition list, contextual alerts, and footer actions. The list stays visible and the selected row keeps its highlight.
                                • Never navigate to a separate page for one record; only deep edits open a dialog or a full page.
                                • Empty results show the EmptyState inside the table card, keeping the filters visible.
                                Application pages

                                Phone app screens

                                The application at 375px: top bar + content + bottom tab bar. Home with stats and a list, a searchable list with filter chips, a detail screen with a sticky action, a form, and a bottom sheet.

                                Product previewPrimary layout
                                375 × 760
                                Home
                                Good morning

                                Maya Chen

                                Open tasks
                                9
                                −2
                                This week
                                Waiting on you
                                3
                                +1
                                Reviews

                                Needs your attention

                                • Approve billing refactorPR #482 · waiting 2hReview
                                • Brand assets v3Shared 3 filesNew
                                • Drawer focus trapAssigned to youBug
                                • Onboarding copyComment · 1h
                                HTML
                                <div class="cn-phone"><header class="cn-phone__bar"><span class="cn-phone__logo" aria-hidden="true"></span><span class="cn-text-label-md cn-phone__title">Home</span><button type="button" class="cn-icon-button" data-variant="ghost" data-size="md" data-shape="square" aria-label="Notifications"><svg class="cn-icon-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg></button></header><main class="cn-phone__body"><div class="cn-stack" data-gap="5" style="padding:var(--cn-space-4)"><div class="cn-stack" data-gap="1"><span class="cn-text-body-sm cn-app__muted">Good morning</span><h1 class="cn-text-heading-md">Maya Chen</h1></div><div class="cn-grid" data-cols="2" data-gap="3" data-keep><div class="cn-stat" data-size="md" data-variant="card" data-trend="down"><div class="cn-stat__label">Open tasks</div><div class="cn-stat__value">9</div><div class="cn-stat__delta"><svg class="cn-stat__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 3v10M4 9l4 4 4-4"/></svg><span>−2</span></div><div class="cn-stat__description">This week</div></div><div class="cn-stat" data-size="md" data-variant="card" data-trend="up"><div class="cn-stat__label">Waiting on you</div><div class="cn-stat__value">3</div><div class="cn-stat__delta"><svg class="cn-stat__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 13V3M4 7l4-4 4 4"/></svg><span>+1</span></div><div class="cn-stat__description">Reviews</div></div></div><div class="cn-row" data-justify="between"><h2 class="cn-text-heading-xs">Needs your attention</h2><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">See all</span></button></div></div><ul class="cn-list" data-density="default" data-variant="plain"><li class="cn-list__item"><span class="cn-list__leading"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="success" data-status="none" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span></span></span><span class="cn-list__content"><span class="cn-list__title">Approve billing refactor</span><span class="cn-list__description">PR #482 · waiting 2h</span></span><span class="cn-list__trailing"><span class="cn-badge" data-tone="warning" data-variant="soft" data-size="sm">Review</span><svg class="cn-list__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span></li><li class="cn-list__item"><span class="cn-list__leading"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="warning" data-status="none" role="img" aria-label="Sofia Almeida"><span class="cn-avatar__fallback" aria-hidden="true">SA</span></span></span><span class="cn-list__content"><span class="cn-list__title">Brand assets v3</span><span class="cn-list__description">Shared 3 files</span></span><span class="cn-list__trailing"><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">New</span><svg class="cn-list__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span></li><li class="cn-list__item"><span class="cn-list__leading"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="info" data-status="none" role="img" aria-label="Lucas Ferreira"><span class="cn-avatar__fallback" aria-hidden="true">LF</span></span></span><span class="cn-list__content"><span class="cn-list__title">Drawer focus trap</span><span class="cn-list__description">Assigned to you</span></span><span class="cn-list__trailing"><span class="cn-badge" data-tone="danger" data-variant="soft" data-size="sm">Bug</span><svg class="cn-list__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span></li><li class="cn-list__item"><span class="cn-list__leading"><span class="cn-avatar" data-size="md" data-shape="circle" data-tone="success" data-status="none" role="img" aria-label="Noah Berg"><span class="cn-avatar__fallback" aria-hidden="true">NB</span></span></span><span class="cn-list__content"><span class="cn-list__title">Onboarding copy</span><span class="cn-list__description">Comment · 1h</span></span><span class="cn-list__trailing"><svg class="cn-list__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4"/></svg></span></li></ul></main><nav class="cn-phone__tabs" aria-label="Main"><a href="#" class="cn-phone__tab" aria-current="page"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg><span>Home</span></a><a href="#" class="cn-phone__tab"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span>Tasks</span></a><a href="#" class="cn-phone__tab"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span>Inbox</span></a><a href="#" class="cn-phone__tab"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><span>Profile</span></a></nav></div>
                                Layout guidelines
                                • Shell: 56px top bar (back or logo, centered title, one icon action), scrolling content with 16px side padding, 64px bottom tab bar with 4–5 items (icon 24 + label 11px); the current tab is brand-colored.
                                • Controls are lg (44px) on phones; primary actions sit in a sticky footer above the tab bar, full width.
                                • Lists are edge-to-edge rows (56px) with avatar, title, one-line description and a chevron; cards only for grouped content.
                                • Filters are horizontal scrolling chips under the search field; never a sidebar or a dropdown filter panel.
                                • Detail screens open as pushed pages (back arrow), quick edits as bottom sheets (drawer side=bottom) with a grab handle.
                                • Everything here is the same components as desktop with data-size="lg"; no separate mobile component set.
                                Application pages

                                Onboarding wizard

                                First-run flow: a focused, single-column card with a step indicator, one question per step, a back/continue footer, and a finishing screen. Also as a split layout with a preview panel.

                                Product previewPrimary layout
                                1280 × 760
                                LumenStep 1 of 4
                                1. 1Your details
                                2. 2Company
                                3. 3Invite team
                                4. 4Done

                                Tell us about you

                                This helps us personalize your workspace.

                                HTML
                                <div class="cn-onb"><header class="cn-onb__head"><span class="cn-onb__logo" aria-hidden="true"></span><span class="cn-text-label-md">Lumen</span><span class="cn-text-body-sm cn-app__muted">Step 1 of 4</span></header><main class="cn-onb__main"><div class="cn-onb__card"><div class="cn-stack" data-gap="8"><ol class="cn-stepper" data-variant="number" aria-label="Setup progress" data-orientation="horizontal" data-size="md"><li class="cn-stepper__step" data-state="current" aria-current="step"><span class="cn-stepper__indicator">1</span><span class="cn-stepper__content"><span class="cn-stepper__label">Your details</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step" data-state="upcoming"><span class="cn-stepper__indicator">2</span><span class="cn-stepper__content"><span class="cn-stepper__label">Company</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step" data-state="upcoming"><span class="cn-stepper__indicator">3</span><span class="cn-stepper__content"><span class="cn-stepper__label">Invite team</span></span><span class="cn-stepper__connector" aria-hidden="true"></span></li><li class="cn-stepper__step" data-state="upcoming"><span class="cn-stepper__indicator">4</span><span class="cn-stepper__content"><span class="cn-stepper__label">Done</span></span></li></ol><div class="cn-stack" data-gap="2"><h1 class="cn-text-heading-lg">Tell us about you</h1><p class="cn-text-body-lg cn-app__muted">This helps us personalize your workspace.</p></div><form class="cn-stack" data-gap="5"><div class="cn-field" data-layout="vertical" data-size="md"><label class="cn-field__label" for="o-name">Full name</label><div class="cn-field__control"><div class="cn-input" data-variant="default" data-size="md"><input class="cn-input__field" id="o-name" type="text" value="Maya Chen"></div></div></div><div class="cn-field" data-layout="vertical" data-size="md"><label class="cn-field__label" for="o-role">What best describes your role?</label><div class="cn-field__control"><div class="cn-select" data-variant="default" data-size="md"><select class="cn-select__field" id="o-role" aria-label="Role"><option>Founder</option><option selected>Product</option><option>Design</option><option>Engineering</option></select><svg class="cn-select__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div></div></div><div class="cn-field" data-layout="vertical" data-size="md"><label class="cn-field__label" for="o-team">Team size</label><div class="cn-field__control"><div class="cn-segmented-control" data-size="md" data-variant="full" role="radiogroup" aria-label="Team size"><button type="button" class="cn-segmented-control__item" role="radio" aria-checked="false">Just me</button><button type="button" class="cn-segmented-control__item" role="radio" aria-checked="true">2–10</button><button type="button" class="cn-segmented-control__item" role="radio" aria-checked="false">11–50</button><button type="button" class="cn-segmented-control__item" role="radio" aria-checked="false">51+</button></div></div></div><footer class="cn-onb__foot"><button type="button" class="cn-button" data-variant="ghost" data-size="lg"><span class="cn-button__label">Back</span></button><button type="button" class="cn-button" data-variant="primary" data-size="lg"><span class="cn-button__label">Continue</span></button></footer></form></div></div></main></div>
                                Layout guidelines
                                • No sidebar. A minimal header (logo, product name, "Step n of 4") and a centered 560px card with 32px padding.
                                • The stepper at the top of the card shows 3–5 steps: complete (check), current (brand), upcoming (muted); never more than 5.
                                • One question per step: heading-lg title, body-lg supporting text, then 1–4 fields (20px apart). Continue is the only primary button; Back is ghost on the left; "Skip for now" as a link when optional.
                                • Invite step: repeatable email rows with a role select and an "Add another" link button; the primary reads "Send invites".
                                • Finishing screen: success featured icon (outline circle), heading-lg, body-lg, one primary "Go to dashboard".
                                Application pages

                                Roles and permissions

                                A roles × permissions matrix with grouped rows, a role detail with its members, and an API keys page.

                                Product previewPrimary layout
                                1280 × 760

                                Roles and permissions

                                Control what each role can do across the workspace.

                                Permission matrix

                                Owners always have every permission.

                                Permission
                                Owner1 people
                                Admin3 people
                                Member14 people
                                Viewer6 people
                                Workspace
                                Manage billing
                                Invite and remove members
                                Change roles
                                Projects
                                Create projects
                                Delete projects
                                Edit any task
                                Data
                                Export data
                                View audit log
                                Integrations
                                Manage API keys
                                Connect apps
                                2 unsaved changes
                                HTML
                                <div class="cn-app"><nav class="cn-sidebar" data-variant="simple" data-theme="light" aria-label="Main"><div class="cn-sidebar__brand"><span class="cn-sidebar__logo" aria-hidden="true"></span><span class="cn-sidebar__brand-name">Lumen</span></div><div class="cn-sidebar__search cn-input" data-variant="default" data-size="md"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search" aria-label="Search"></div><div class="cn-sidebar__nav"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg><span class="cn-sidebar__label">Home</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><span class="cn-sidebar__label">Dashboard</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-sidebar__label">Projects</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-sidebar__label">Tasks</span><span class="cn-sidebar__badge">10</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-sidebar__label">Reporting</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><span class="cn-sidebar__label">Users</span></a></div><div class="cn-sidebar__footer"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg><span class="cn-sidebar__label">Support</span></a><a href="#" class="cn-sidebar__item" aria-current="page"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg><span class="cn-sidebar__label">Settings</span></a><div class="cn-sidebar__account"><span class="cn-sidebar__avatar" aria-hidden="true">MC</span><div class="cn-sidebar__account-text"><span class="cn-sidebar__name">Maya Chen</span><span class="cn-sidebar__email">maya@lumen.co</span></div><button type="button" class="cn-sidebar__account-action" aria-label="Log out"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button></div></div></nav><main class="cn-app__main"><header class="cn-page-header" data-variant="simple" data-divider="yes"><div class="cn-page-header__content"><h1 class="cn-page-header__title">Roles and permissions</h1><p class="cn-page-header__description">Control what each role can do across the workspace.</p></div><div class="cn-page-header__actions"><button type="button" class="cn-button" data-variant="outline" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg><span class="cn-button__label">New role</span></button></div></header><div class="cn-tabs" data-variant="underline" data-size="md" role="tablist"><button type="button" class="cn-tabs__tab" role="tab" aria-selected="true">Permissions</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">Roles</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">API keys</button></div><section class="cn-card cn-app__table-card" data-variant="default" data-padding="none"><div class="cn-section-header" data-variant="card" data-divider="yes"><div class="cn-section-header__content"><h2 class="cn-section-header__title">Permission matrix</h2><p class="cn-section-header__description">Owners always have every permission.</p></div><div class="cn-section-header__actions"><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">Learn about roles</span></button></div></div><div class="cn-card__body cn-app__card-body cn-app__card-body--flush" role="region" tabindex="0" aria-label="Scrollable table"><table class="cn-table cn-perm__table" data-density="compact" data-variant="default"><thead class="cn-table__head"><tr class="cn-table__row"><th class="cn-table__header-cell" scope="col">Permission</th><th class="cn-table__header-cell" scope="col" data-align="center"><div class="cn-stack cn-center" data-gap="1" data-align="center"><span>Owner</span><span class="cn-text-body-xs cn-app__muted">1 people</span></div></th><th class="cn-table__header-cell" scope="col" data-align="center"><div class="cn-stack cn-center" data-gap="1" data-align="center"><span>Admin</span><span class="cn-text-body-xs cn-app__muted">3 people</span></div></th><th class="cn-table__header-cell" scope="col" data-align="center"><div class="cn-stack cn-center" data-gap="1" data-align="center"><span>Member</span><span class="cn-text-body-xs cn-app__muted">14 people</span></div></th><th class="cn-table__header-cell" scope="col" data-align="center"><div class="cn-stack cn-center" data-gap="1" data-align="center"><span>Viewer</span><span class="cn-text-body-xs cn-app__muted">6 people</span></div></th></tr></thead><tbody class="cn-table__body"><tr class="cn-table__row cn-perm__group"><td class="cn-table__cell" colspan="5">Workspace</td></tr><tr class="cn-table__row"><td class="cn-table__cell"><span class="cn-text-body-sm">Manage billing</span></td><td class="cn-table__cell" data-align="center"><span class="cn-perm__lock" aria-label="Always on"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked aria-label="Manage billing for Admin"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Manage billing for Admin</span></label></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Manage billing for Member"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Manage billing for Member</span></label></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Manage billing for Viewer"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Manage billing for Viewer</span></label></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><span class="cn-text-body-sm">Invite and remove members</span></td><td class="cn-table__cell" data-align="center"><span class="cn-perm__lock" aria-label="Always on"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked aria-label="Invite and remove members for Admin"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Invite and remove members for Admin</span></label></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Invite and remove members for Member"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Invite and remove members for Member</span></label></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Invite and remove members for Viewer"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Invite and remove members for Viewer</span></label></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><span class="cn-text-body-sm">Change roles</span></td><td class="cn-table__cell" data-align="center"><span class="cn-perm__lock" aria-label="Always on"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Change roles for Admin"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Change roles for Admin</span></label></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Change roles for Member"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Change roles for Member</span></label></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Change roles for Viewer"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Change roles for Viewer</span></label></td></tr><tr class="cn-table__row cn-perm__group"><td class="cn-table__cell" colspan="5">Projects</td></tr><tr class="cn-table__row"><td class="cn-table__cell"><span class="cn-text-body-sm">Create projects</span></td><td class="cn-table__cell" data-align="center"><span class="cn-perm__lock" aria-label="Always on"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked aria-label="Create projects for Admin"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Create projects for Admin</span></label></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked aria-label="Create projects for Member"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Create projects for Member</span></label></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Create projects for Viewer"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Create projects for Viewer</span></label></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><span class="cn-text-body-sm">Delete projects</span></td><td class="cn-table__cell" data-align="center"><span class="cn-perm__lock" aria-label="Always on"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked aria-label="Delete projects for Admin"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Delete projects for Admin</span></label></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Delete projects for Member"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Delete projects for Member</span></label></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Delete projects for Viewer"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Delete projects for Viewer</span></label></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><span class="cn-text-body-sm">Edit any task</span></td><td class="cn-table__cell" data-align="center"><span class="cn-perm__lock" aria-label="Always on"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked aria-label="Edit any task for Admin"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Edit any task for Admin</span></label></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked aria-label="Edit any task for Member"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Edit any task for Member</span></label></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Edit any task for Viewer"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Edit any task for Viewer</span></label></td></tr><tr class="cn-table__row cn-perm__group"><td class="cn-table__cell" colspan="5">Data</td></tr><tr class="cn-table__row"><td class="cn-table__cell"><span class="cn-text-body-sm">Export data</span></td><td class="cn-table__cell" data-align="center"><span class="cn-perm__lock" aria-label="Always on"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked aria-label="Export data for Admin"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Export data for Admin</span></label></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Export data for Member"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Export data for Member</span></label></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Export data for Viewer"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Export data for Viewer</span></label></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><span class="cn-text-body-sm">View audit log</span></td><td class="cn-table__cell" data-align="center"><span class="cn-perm__lock" aria-label="Always on"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked aria-label="View audit log for Admin"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">View audit log for Admin</span></label></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="View audit log for Member"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">View audit log for Member</span></label></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="View audit log for Viewer"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">View audit log for Viewer</span></label></td></tr><tr class="cn-table__row cn-perm__group"><td class="cn-table__cell" colspan="5">Integrations</td></tr><tr class="cn-table__row"><td class="cn-table__cell"><span class="cn-text-body-sm">Manage API keys</span></td><td class="cn-table__cell" data-align="center"><span class="cn-perm__lock" aria-label="Always on"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked aria-label="Manage API keys for Admin"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Manage API keys for Admin</span></label></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Manage API keys for Member"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Manage API keys for Member</span></label></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Manage API keys for Viewer"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Manage API keys for Viewer</span></label></td></tr><tr class="cn-table__row"><td class="cn-table__cell"><span class="cn-text-body-sm">Connect apps</span></td><td class="cn-table__cell" data-align="center"><span class="cn-perm__lock" aria-label="Always on"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked aria-label="Connect apps for Admin"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Connect apps for Admin</span></label></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" checked aria-label="Connect apps for Member"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Connect apps for Member</span></label></td><td class="cn-table__cell" data-align="center"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Connect apps for Viewer"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Connect apps for Viewer</span></label></td></tr></tbody></table></div><footer class="cn-card__footer"><div class="cn-row" data-justify="between"><span class="cn-text-body-sm cn-app__muted">2 unsaved changes</span><div class="cn-row" data-gap="2"><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Discard</span></button><button type="button" class="cn-button" data-variant="primary" data-size="sm"><span class="cn-button__label">Save changes</span></button></div></div></footer></section></main></div>
                                Layout guidelines
                                • Matrix: permissions as rows grouped by area (group rows in bg-subtle, label-xs), roles as columns with the member count under the name; cells are checkboxes, the Owner column is locked (check icon, no control).
                                • Changes are saved explicitly: a sticky footer with "Discard" and "Save changes" appears when anything changed.
                                • Role detail: description, members list (avatar, name, email, remove) and an "Add members" primary; deleting a role requires moving its members first.
                                • API keys: table with name, key prefix in code style, created, last used, and a Revoke ghost-danger action; new keys are shown once in a dialog with a copy button.
                                Application pages

                                Profile page

                                A person or entity page: cover band, avatar + identity + actions, tabs, then a two-column body (about card + activity) or a stats row. Also the public/compact variants.

                                Product previewPrimary layout
                                1280 × 760

                                Maya Chen Admin

                                Product Manager · Florianópolis, Brazil · Joined March 2024

                                About

                                Email
                                maya@lumen.co
                                Role
                                Product Manager
                                Teams
                                ProductDesignLeadership
                                Time zone
                                GMT−3 · 14:22 local
                                Bio
                                I’m a product manager based in Florianópolis. I like building things that make people’s work easier.

                                Recent projects

                                • Website redesignMarketing · On trackOn track
                                • Billing platformEngineering · On trackOn track
                                • Mobile app 2.0Product · At riskAt risk
                                Tasks completed
                                128
                                +12 this week
                                Last 30 days
                                Reviews
                                24
                                −3
                                Last 30 days

                                Works with

                                Daniel CostaEngineering
                                Sofia AlmeidaDesign
                                Lucas FerreiraFrontend
                                HTML
                                <div class="cn-app"><nav class="cn-sidebar" data-variant="simple" data-theme="light" aria-label="Main"><div class="cn-sidebar__brand"><span class="cn-sidebar__logo" aria-hidden="true"></span><span class="cn-sidebar__brand-name">Lumen</span></div><div class="cn-sidebar__search cn-input" data-variant="default" data-size="md"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search" aria-label="Search"></div><div class="cn-sidebar__nav"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg><span class="cn-sidebar__label">Home</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><span class="cn-sidebar__label">Dashboard</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-sidebar__label">Projects</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-sidebar__label">Tasks</span><span class="cn-sidebar__badge">10</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-sidebar__label">Reporting</span></a><a href="#" class="cn-sidebar__item" aria-current="page"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><span class="cn-sidebar__label">Users</span></a></div><div class="cn-sidebar__footer"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg><span class="cn-sidebar__label">Support</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg><span class="cn-sidebar__label">Settings</span></a><div class="cn-sidebar__account"><span class="cn-sidebar__avatar" aria-hidden="true">MC</span><div class="cn-sidebar__account-text"><span class="cn-sidebar__name">Maya Chen</span><span class="cn-sidebar__email">maya@lumen.co</span></div><button type="button" class="cn-sidebar__account-action" aria-label="Log out"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button></div></div></nav><main class="cn-app__main cn-profile"><div class="cn-profile__cover"></div><header class="cn-profile__head"><div class="cn-profile__identity"><span class="cn-avatar" data-size="2xl" data-shape="circle" data-tone="accent" data-status="online" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span><span class="cn-avatar__status"></span></span><div class="cn-stack" data-gap="1"><h1 class="cn-text-heading-lg cn-row" data-gap="2">Maya Chen <span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Admin</span></h1><p class="cn-text-body-lg cn-app__muted">Product Manager · Florianópolis, Brazil · Joined March 2024</p></div></div><div class="cn-row" data-gap="3"><button type="button" class="cn-button" data-variant="outline" data-size="md"><svg class="cn-button__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-button__label">Message</span></button><button type="button" class="cn-button" data-variant="primary" data-size="md"><span class="cn-button__label">Edit profile</span></button></div></header><div class="cn-tabs" data-variant="underline" data-size="md" role="tablist"><button type="button" class="cn-tabs__tab" role="tab" aria-selected="true">Overview</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">Activity</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">Projects</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">Settings</button></div><div class="cn-grid" data-cols="3" data-gap="6"><div class="cn-app__span2 cn-stack" data-gap="6"><section class="cn-card" data-variant="default" data-padding="none"><div class="cn-section-header" data-variant="card" data-divider="yes"><div class="cn-section-header__content"><h2 class="cn-section-header__title">About</h2></div></div><div class="cn-card__body cn-app__card-body"><dl class="cn-profile__dl"><div><dt>Email</dt><dd>maya@lumen.co</dd></div><div><dt>Role</dt><dd>Product Manager</dd></div><div><dt>Teams</dt><dd><div class="cn-row" data-gap="1"><span class="cn-badge" data-tone="info" data-variant="soft" data-size="sm">Product</span><span class="cn-badge" data-tone="accent" data-variant="soft" data-size="sm">Design</span><span class="cn-badge" data-tone="neutral" data-variant="soft" data-size="sm">Leadership</span></div></dd></div><div><dt>Time zone</dt><dd>GMT−3 · 14:22 local</dd></div><div><dt>Bio</dt><dd>I’m a product manager based in Florianópolis. I like building things that make people’s work easier.</dd></div></dl></div></section><section class="cn-card" data-variant="default" data-padding="none"><div class="cn-section-header" data-variant="card" data-divider="yes"><div class="cn-section-header__content"><h2 class="cn-section-header__title">Recent projects</h2></div><div class="cn-section-header__actions"><button type="button" class="cn-button" data-variant="link-color" data-size="sm"><span class="cn-button__label">View all</span></button></div></div><div class="cn-card__body cn-app__card-body"><ul class="cn-list" data-density="default" data-variant="plain"><li class="cn-list__item"><span class="cn-list__content"><span class="cn-list__title">Website redesign</span><span class="cn-list__description">Marketing · On track</span></span><span class="cn-list__trailing"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>On track</span></span></li><li class="cn-list__item"><span class="cn-list__content"><span class="cn-list__title">Billing platform</span><span class="cn-list__description">Engineering · On track</span></span><span class="cn-list__trailing"><span class="cn-badge" data-tone="success" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>On track</span></span></li><li class="cn-list__item"><span class="cn-list__content"><span class="cn-list__title">Mobile app 2.0</span><span class="cn-list__description">Product · At risk</span></span><span class="cn-list__trailing"><span class="cn-badge" data-tone="warning" data-variant="soft" data-size="sm"><span class="cn-badge__dot"></span>At risk</span></span></li></ul></div></section></div><div class="cn-stack" data-gap="6"><div class="cn-grid" data-cols="1" data-gap="4"><div class="cn-stat" data-size="md" data-variant="card" data-trend="up"><div class="cn-stat__label">Tasks completed</div><div class="cn-stat__value">128</div><div class="cn-stat__delta"><svg class="cn-stat__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 13V3M4 7l4-4 4 4"/></svg><span>+12 this week</span></div><div class="cn-stat__description">Last 30 days</div></div><div class="cn-stat" data-size="md" data-variant="card" data-trend="down"><div class="cn-stat__label">Reviews</div><div class="cn-stat__value">24</div><div class="cn-stat__delta"><svg class="cn-stat__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 3v10M4 9l4 4 4-4"/></svg><span>−3</span></div><div class="cn-stat__description">Last 30 days</div></div></div><section class="cn-card" data-variant="default" data-padding="none"><div class="cn-section-header" data-variant="card" data-divider="yes"><div class="cn-section-header__content"><h2 class="cn-section-header__title">Works with</h2></div></div><div class="cn-card__body cn-app__card-body"><div class="cn-stack" data-gap="3"><div class="cn-row" data-gap="3"><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="success" data-status="none" role="img" aria-label="Daniel Costa"><span class="cn-avatar__fallback" aria-hidden="true">DC</span></span><div class="cn-stack" data-gap="0"><span class="cn-text-label-sm">Daniel Costa</span><span class="cn-text-body-sm cn-app__muted">Engineering</span></div></div><div class="cn-row" data-gap="3"><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="warning" data-status="none" role="img" aria-label="Sofia Almeida"><span class="cn-avatar__fallback" aria-hidden="true">SA</span></span><div class="cn-stack" data-gap="0"><span class="cn-text-label-sm">Sofia Almeida</span><span class="cn-text-body-sm cn-app__muted">Design</span></div></div><div class="cn-row" data-gap="3"><span class="cn-avatar" data-size="sm" data-shape="circle" data-tone="info" data-status="none" role="img" aria-label="Lucas Ferreira"><span class="cn-avatar__fallback" aria-hidden="true">LF</span></span><div class="cn-stack" data-gap="0"><span class="cn-text-label-sm">Lucas Ferreira</span><span class="cn-text-body-sm cn-app__muted">Frontend</span></div></div></div></div></section></div></div></main></div>
                                Layout guidelines
                                • Cover band 160px in bg-action-subtle (or an image), avatar 96px (2xl) overlapping it by half, identity block right of the avatar, actions right-aligned; one primary (Edit profile / Follow).
                                • Tabs under the header: Overview, Activity, Projects, Settings.
                                • Body: 2:1 grid — main column (about card with description-list, projects table/list) and side column (stats, teams, contact).
                                • Never repeat the name in the page header; the identity block is the h1.
                                Application pages

                                Settings page

                                Account and workspace settings: page header + tabs, then sections laid out as label-column / control-column rows separated by hairlines, with the actions at the end. Variants: profile form, team, plan & billing, notifications.

                                Product previewPrimary layout
                                1280 × 760

                                Settings

                                Manage your account settings and preferences.

                                Personal info

                                Update your photo and personal details here.

                                NameShown on your profile and in mentions.
                                Email addressUsed for sign-in and notifications.
                                Your photoThis will be displayed on your profile.
                                Click to upload or drag and dropSVG, PNG, JPG or GIF (max. 800×400px)
                                RoleYour title inside the workspace.
                                CountryWhere you are based, for tax and time zone.
                                BioWrite a short introduction.
                                275 characters left
                                HTML
                                <div class="cn-app"><nav class="cn-sidebar" data-variant="simple" data-theme="light" aria-label="Main"><div class="cn-sidebar__brand"><span class="cn-sidebar__logo" aria-hidden="true"></span><span class="cn-sidebar__brand-name">Lumen</span></div><div class="cn-sidebar__search cn-input" data-variant="default" data-size="md"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg><input class="cn-input__field" type="search" placeholder="Search" aria-label="Search"></div><div class="cn-sidebar__nav"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5L8 3l5.5 4.5V13a.5.5 0 01-.5.5H3a.5.5 0 01-.5-.5V7.5z"/></svg><span class="cn-sidebar__label">Home</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 7h11M5.5 2v3M10.5 2v3"/></svg><span class="cn-sidebar__label">Dashboard</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg><span class="cn-sidebar__label">Projects</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg><span class="cn-sidebar__label">Tasks</span><span class="cn-sidebar__badge">10</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><span class="cn-sidebar__label">Reporting</span></a><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 13.5c.6-2.6 2.6-4 5.25-4s4.65 1.4 5.25 4"/></svg><span class="cn-sidebar__label">Users</span></a></div><div class="cn-sidebar__footer"><a href="#" class="cn-sidebar__item"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M8 7v4M8 5v.5"/></svg><span class="cn-sidebar__label">Support</span></a><a href="#" class="cn-sidebar__item" aria-current="page"><svg class="cn-sidebar__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="2"/><path d="M8 1.75v1.5M8 12.75v1.5M1.75 8h1.5M12.75 8h1.5M3.6 3.6l1.05 1.05M11.35 11.35l1.05 1.05M3.6 12.4l1.05-1.05M11.35 4.65l1.05-1.05"/></svg><span class="cn-sidebar__label">Settings</span></a><div class="cn-sidebar__account"><span class="cn-sidebar__avatar" aria-hidden="true">MC</span><div class="cn-sidebar__account-text"><span class="cn-sidebar__name">Maya Chen</span><span class="cn-sidebar__email">maya@lumen.co</span></div><button type="button" class="cn-sidebar__account-action" aria-label="Log out"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3.5H3.5v9h9V9M9.5 3h3.5v3.5M13 3L7.5 8.5"/></svg></button></div></div></nav><main class="cn-app__main"><header class="cn-page-header" data-variant="simple" data-divider="yes"><div class="cn-page-header__content"><h1 class="cn-page-header__title">Settings</h1><p class="cn-page-header__description">Manage your account settings and preferences.</p></div><div class="cn-page-header__actions"><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Cancel</span></button><button type="button" class="cn-button" data-variant="primary" data-size="md"><span class="cn-button__label">Save changes</span></button></div></header><div class="cn-tabs" data-variant="underline" data-size="md" role="tablist"><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">My details</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="true">Profile</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">Password</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">Team</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">Plan</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">Billing</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">Notifications</button><button type="button" class="cn-tabs__tab" role="tab" aria-selected="false">Integrations</button></div><div class="cn-stack" data-gap="0" style="max-width:1024px"><div class="cn-section-header" data-variant="default" data-divider="yes"><div class="cn-section-header__content"><h2 class="cn-section-header__title">Personal info</h2><p class="cn-section-header__description">Update your photo and personal details here.</p></div><div class="cn-section-header__actions"><button type="button" class="cn-button" data-variant="outline" data-size="sm"><span class="cn-button__label">Cancel</span></button><button type="button" class="cn-button" data-variant="primary" data-size="sm"><span class="cn-button__label">Save</span></button></div></div><div class="cn-app__form-row"><div class="cn-app__form-label"><span>Name</span><span>Shown on your profile and in mentions.</span></div><div class="cn-stack" data-gap="4"><div class="cn-grid" data-cols="2" data-gap="4" data-keep><div class="cn-input" data-variant="default" data-size="md"><input class="cn-input__field" id="s-first" type="text" value="Maya"></div><div class="cn-input" data-variant="default" data-size="md"><input class="cn-input__field" id="s-last" type="text" value="Chen"></div></div></div></div><div class="cn-app__form-row"><div class="cn-app__form-label"><span>Email address</span><span>Used for sign-in and notifications.</span></div><div class="cn-stack" data-gap="4"><div class="cn-input" data-variant="default" data-size="md"><svg class="cn-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5V4a1 1 0 011-1h9a1 1 0 011 1v5.5M2.5 9.5V12a1 1 0 001 1h9a1 1 0 001-1V9.5"/></svg><input class="cn-input__field" id="s-email" type="email" value="maya@lumen.co"></div></div></div><div class="cn-app__form-row"><div class="cn-app__form-label"><span>Your photo</span><span>This will be displayed on your profile.</span></div><div class="cn-stack" data-gap="4"><div class="cn-row" data-gap="5" data-align="start"><span class="cn-avatar" data-size="2xl" data-shape="circle" data-tone="accent" data-status="none" role="img" aria-label="Maya Chen"><span class="cn-avatar__fallback" aria-hidden="true">MC</span></span><div class="cn-file-dropzone cn-flex-1" data-size="sm"><input class="cn-file-dropzone__input" type="file" aria-label="Upload photo"><span class="cn-file-dropzone__icon" aria-hidden="true"><svg class="cn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3.5a1 1 0 00-1-1h-6a1 1 0 00-1 1v6a1 1 0 001 1h2"/></svg></span><span class="cn-file-dropzone__title"><strong>Click to upload</strong> or drag and drop</span><span class="cn-file-dropzone__hint">SVG, PNG, JPG or GIF (max. 800×400px)</span></div></div></div></div><div class="cn-app__form-row"><div class="cn-app__form-label"><span>Role</span><span>Your title inside the workspace.</span></div><div class="cn-stack" data-gap="4"><div class="cn-input" data-variant="default" data-size="md"><input class="cn-input__field" id="s-role" type="text" value="Product Manager"></div></div></div><div class="cn-app__form-row"><div class="cn-app__form-label"><span>Country</span><span>Where you are based, for tax and time zone.</span></div><div class="cn-stack" data-gap="4"><div class="cn-select" data-variant="default" data-size="md"><select class="cn-select__field" id="s-country" aria-label="Country"><option>Brazil</option><option selected>United States</option><option>Portugal</option></select><svg class="cn-select__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></div></div></div><div class="cn-app__form-row"><div class="cn-app__form-label"><span>Bio</span><span>Write a short introduction.</span></div><div class="cn-stack" data-gap="4"><div class="cn-textarea" data-size="md" data-resize="vertical"><textarea class="cn-textarea__field" rows="4" aria-label="Bio">I’m a product manager based in Florianópolis. I like building things that make people’s work easier.</textarea></div><span class="cn-text-body-sm cn-app__muted">275 characters left</span></div></div><div class="cn-app__form-actions"><button type="button" class="cn-button" data-variant="outline" data-size="md"><span class="cn-button__label">Cancel</span></button><button type="button" class="cn-button" data-variant="primary" data-size="md"><span class="cn-button__label">Save changes</span></button></div></div></main></div>
                                Layout guidelines
                                • Tabs under the page header select the settings area; the content is a single 1024px-max column.
                                • Each setting is a row: left column 280px with a semibold label and a one-line hint; right column the control(s), max 512px. Rows separated by 20px padding and a hairline.
                                • Section header (heading-sm + description + actions) at the top of each area; the save actions repeat at the bottom of long forms.
                                • Destructive actions (delete account) sit in their own last section with a danger-outline button.
                                • Team: section header with count badge, a table with checkbox, avatar, status, role, email and actions, then pagination.
                                Shared pages

                                Auth and error pages

                                Log in, sign up, forgot password, verification code and 404 — centered single-column cards (360px) or split with an image. Shared across every product.

                                Product previewPrimary layout
                                1280 × 760

                                Log in to your account

                                Welcome back! Please enter your details.

                                Forgot password

                                Don’t have an account? Sign up

                                HTML
                                <div class="cn-auth"><div class="cn-auth__card"><div class="cn-stack cn-center" data-gap="3" data-align="center"><span class="cn-auth__logo" aria-hidden="true"></span><h1 class="cn-text-heading-lg">Log in to your account</h1><p class="cn-text-body-lg cn-auth__muted">Welcome back! Please enter your details.</p></div><form class="cn-stack" data-gap="5"><div class="cn-field" data-layout="vertical" data-size="md"><label class="cn-field__label" for="l-email">Email</label><div class="cn-field__control"><div class="cn-input" data-variant="default" data-size="md"><input class="cn-input__field" id="l-email" type="email" placeholder="Enter your email"></div></div></div><div class="cn-field" data-layout="vertical" data-size="md"><label class="cn-field__label" for="l-pass">Password</label><div class="cn-field__control"><div class="cn-input" data-variant="default" data-size="md"><input class="cn-input__field" id="l-pass" type="password" placeholder="••••••••"></div></div></div><div class="cn-row" data-justify="between"><label class="cn-checkbox" data-size="sm"><input class="cn-checkbox__input" type="checkbox" aria-label="Remember for 30 days"><span class="cn-checkbox__control" aria-hidden="true"><svg class="cn-checkbox__indicator" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg></span><span class="cn-checkbox__label">Remember for 30 days</span></label><a href="#" class="cn-link" data-variant="standalone">Forgot password</a></div><div class="cn-stack" data-gap="3"><button type="button" class="cn-button" style="width:100%" data-variant="primary" data-size="lg"><span class="cn-button__label">Sign in</span></button><button type="button" class="cn-button" data-variant="outline" data-size="lg" style="width:100%"><svg class="cn-button__icon" viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="6.5" fill="none" stroke="currentColor" stroke-width="1.5"/><path d="M8 5v6M5 8h6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg><span class="cn-button__label">Sign in with Google</span></button></div></form><p class="cn-text-body-sm cn-auth__muted cn-center">Don’t have an account? <a href="#" class="cn-link" data-variant="standalone">Sign up</a></p></div></div>
                                Layout guidelines
                                • One column, 360px wide, centered vertically and horizontally; logo, heading-lg title, body-lg supporting text, then the form.
                                • Form: fields 20px apart, full-width lg primary button, optional social button below it, then the switch link ("Don’t have an account?").
                                • Split variant: form on the left inside a 1:1 split, image bleeding to the right edge, footer with copyright and support email.
                                • Verification: 4 (or 6) 64px code inputs, 8px apart, with a resend link.
                                • 404: a brand-colored "404 error" kicker, display-lg title, body-xl text, two buttons (outline "Go back", primary "Take me home").