Design SystemDocumentation

Building the catalog

How the design-system catalog is built — the authoring guide plus the .ds-doc-* primitives every component card is made of (viewport, asset cells, lightbox). Press ⌘K to search the whole catalog from here.

Start Here
Documentation
A visual stylebook for the docs themselves. Every rule baked into the lifi-ds-docs skill — typography, label shapes, caption length, pane-aware primitives, syntax tokens, auto-spacing — rendered here at real size so a designer can see what shapes the page and tune it deliberately. Each card below uses the rule it documents. When adding or migrating a component, mirror these shapes — don't invent new ones.

Skill & Workflow

Authoring guide

How to add or migrate a component in the catalog. Hands you off to the lifi-ds-docs skill — the canonical procedure.

python3 .claude/skills/lifi-ds-docs/scripts/validate-doc.py

Audience & Why

Audience priority

Designers read the catalog as an atlas; engineers as a code reference; marketing as a brand reference. Designer scanning speed wins.

  1. Designers — scan the catalog as a visual atlas. Recognise components by label + a glance at the preview.
  2. Engineering — arrive with the component name in mind. Click into Markup / Specs / Source for copy-paste code.
  3. Marketing — treat the catalog as a brand reference. Real previews, token-only colours, accent treatments from the live palette.

Typography

Label .ds-doc-card__label

The h3-scale heading at the top of every card. Designers read it first — sized for pre-attentive recognition, not legibility.

Brand button .btn-primary

Variant matrix

Token chips & chain badges .token-chip · .chain-badge

Caption .ds-doc-card__caption

One sentence under 140 chars in plain language — lead with the component's job, follow with when to reach for it.

Brand button .btn-primary

Brand action button. Three sizes; .btn-lg in heroes, .btn-sm in dense rails.

Brand button

Powerful and beautiful action buttons that elevate your CTAs and seamlessly delight users.

Pane Primitives

Pane primitives .ds-doc-* family · 16 approved blocks

The complete vocabulary for documentation panes — containers, prose, labels, inline, layout, demo. Use these instead of inline style.

Section heading

Standard .ds-doc-text paragraph. Inline <code> like .btn-primary auto-renders as a chip via the .code-chip universal rule. Multiple paragraphs auto-space.

Use <strong> for emphasis — it darkens to --text-secondary automatically.

  • .ds-doc-list — body-size text, tight item spacing.
  • Bold leads — start an item with <strong> for "X — does Y" pairs.
  • Inline .classes chip the same way as in paragraphs.

.ds-doc-aside — default brand-emphasis aside. Use when the note matters to the surrounding decision.

--note — informational. Background context, links to deeper docs, "by the way" footnotes that aren't decisions.

--success — confirmed-good practice. Use when calling out the recommended path or a working pattern.

--warn — caution. Authoring choice that breaks a downstream contract or has a known foot-gun.

--danger — don't. Anti-patterns and forbidden moves; the variant signals that the rule is non-negotiable.

--muted — quiet footnote. Background-less; reads as a side-note inside busier panes.

// .ds-doc-codeblock — drops chrome inside a pane, keeps it standalone.
const chip = "foundations";
ClassUse
.btn-primaryPrimary action — the dominant CTA
.btn-secondaryPaired secondary on dual-action surfaces

Default

.ds-doc-eyebrow

Active

--accent

Lower

--ghost


Family · caption

.ds-doc-caption — sans, left-aligned, muted; the quieter sibling of .ds-doc-text. Three modifiers carry the placement rhythms used across the catalog (May 2026 promotion: collapsed 82 inline style overrides).

Default · 8 px top

Caption follows the demo with the default 8 px top gap.

--lead · 12 px top

Bigger top gap when the default 8 px feels cramped against the demo above.

--label · acts as label above

Default — caption labels the demo below it.

--centered · figcaption

.brand-tile · 240 px

Family · demo

Four primitives for the kinds of inline annotation that surround a visual demo — a bigger label below an eyebrow, a quiet framed sample box, a right-pinned mono detail, and a smaller-text body modifier.

Priority 1

Designers

Body-lg / weight 600 — the canonical demo headline. Quieter than a card label so it doesn't compete with the card's own heading.

A

Brand button .btn-primary

The --sm modifier drops to body size for tighter rows where the headline sits on one line beside an inline class chip.

Sunk

default

Raised

--raised

Elevated

--elevated

Alpha

--alpha

  • Card primitive surfacestyles.css:14092
  • Brand buttonv4.2 · 12 specs
  • Spot iconjust shipped

Default .ds-doc-text — body size (16px), text-secondary, generous line height. The standard for documentation paragraphs.

The --sm modifier drops to 14px while keeping the same colour and line-height. Use under a headline or caption for context that's quieter than body prose — typical at "Why" / "When to use" notes.

Syntax-highlight tokens · 8 spans

Colour-code source snippets with these eight canonical spans. Don't invent new ones — they cover HTML, CSS, JS, JSON, and shell.

// Eight spans cover every language we ship — HTML, CSS, JS, JSON, shell.
const chip = "foundations";        // keyword + string
if (chip === true) {
  fetch("/api/scopes");          // path
}

<button class="btn-primary" disabled>
  Click, when ready
</button>

Eyebrow-labelled row .ds-doc-eyebrow-row

Pairs a .ds-doc-eyebrow caption with a demo. Stack 3-7 inside a Preview pane to show variants without nested sub-viewports.

Three rows below, each pairing an eyebrow caption with a small demo. Adjacent rows auto-space (32 px); the eyebrow → demo gap is fixed (12 px). No per-instance margins required.

Variant A · default

Variant B · ghost

Variant C · destructive

Colors tab pattern data-tab="colors"

Optional designer-facing pane for state colours, overrides, and critical highlights. Slot 2 — after Preview; validator E016 enforces.

Three flag tones — the taxonomy at a glance

Each swatch in a Colors tab carries a source flag classifying where the colour comes from. The three flags answer the question "is this from the system, is it a deliberate divergence, or is it locked to this widget?"

Inherited
Foundation token via slot
var(--accent-primary) .btn-primary · .chip-accent1 · .status-dot
Colour flows from a foundation token (palette accent, semantic state, surface, text) through a component slot with no scoped remap. The default and most common flag — read: "comes from the system."
Override
Scoped recipe or remap
color-mix(...) · --slot remap .checkbox input:checked · .btn-destructive · .tile.is-active
The rule constructs a new recipe (a color-mix(…) blend, a remapped slot, a per-component shadow) that diverges from the system default. Read: "this component made a deliberate choice."
Convention
Locked per-widget literal
oklch(100% 0 0 / 0.05) · #FFFFFF · brand pair .fab:hover halo · .btn-neutral · .brand-tile
A palette-independent literal locked to this widget — brand tile per-brand colours, alpha-overlay buttons, FAB hover ring, modal scrim. Read: "this colour is per-widget, do not import elsewhere."

Canonical tab strip — Colors at slot 2

When present, the Colors tab sits in slot 2 — immediately after Preview and before Markup. The validator (E016) errors on any other position. The full canonical order is Preview / Colors / Markup / Specs / Anatomy / Rules / Source — Colors is the only optional one.

Designer scan path: Preview → Colors (the small palette that gives the component its identity). Engineer reference begins at Markup. Audience priority (CLAUDE.md) keeps Colors in front of the engineer-facing tabs.

Structure & Spacing

Section + card spacing

Three nested layers, each with its own auto-recipe. Don't author dividers, <hr>, or extra spacers — the CSS handles every layer.

// 02 — My Section

My section

Section description. Sets context for the cards below.

PRIMARY VARIANTS

Card 1 — first variant

Caption.

Card 2 — second variant

Caption.

  • 240px section padding
  • .ds-doc-subsection · hairline + 80px above
  • 80px card-to-card gap + 1px hairline

Variant grouping in Preview panes

When a card carries 3+ variants spanning different axes, group them by axis and stack vertically with .ds-doc-divider--loose between zones. Never mix unrelated variants side-by-side in a grid to save vertical space.

Side-by-side comparison of the two layouts. The left mixes variants from three different concerns into one grid; the right groups them by axis with hairline dividers between zones. Same variants, different scanability — the right one tells the reader at a glance that the component varies on three axes.

Don't · mixed wrap-grid

Content A

Shape A

Content B

Position A

Shape B

Content C

Reader has to parse every eyebrow to figure out what axes the component varies on. The grid optimizes for vertical density, not for cognitive grouping.

Do · zones + dividers

Content A

Content B

Content C


Shape A

Shape B


Position A

Three zones visible at a glance: the component varies on content, shape, and position. Hairlines tell the reader "this is a different axis."

Chapter strip .ds-doc-section-toc

Auto-generated horizontal chapter nav at the top of any section with 2+ chapter markers. Author the markers; the JS renders the strip.

A simulated rendering — what gets prepended to a section's .container when the JS detects 2+ chapter markers. Live examples are at the top of #inputs, #hero, #badges, and at the top of this Authoring Guide. (The Cards group used to live in one chaptered #cards section; it was promoted to a top-level sidebar group of sibling sections Apr 2026 — the chapters became sections, each able to chapter on its own as it grows.)

Active link tracks the chapter the reader is currently scrolled into via IntersectionObserver with rootMargin: -80px 0px -55% 0px.

Page Chrome

Tab nav .tab-nav

Sticky horizontal rail of section anchors. Three sizes; the active link is highlighted by an IntersectionObserver on the page's own JS.

.tab-nav-sm · 40 px tall · 13 px text — dense surfaces, sub-section navs

Default · 56 px tall · 14 px text — most pages

.tab-nav-lg · 72 px tall · 16 px text — primary page nav (every DS sub-page uses this)

Live on the dashboard, AI field manual, brand guide, and competitor analysis pages — all four use .tab-nav-lg.

Resources
Viewport
The canonical preview surface for showcasing DS components in realistic contexts. One primitive, one visual language, extensible via sub-components and modifier classes. Supersedes the historical .ds-doc-card-demo / .ds-doc-tile-demo / per-page ad-hoc <div style="background:…"> wrappers — migrate new demos to .ds-doc-viewport; existing instances stay until touched.

Tabbed viewport — .ds-doc-viewport--tabbed

Multi-pane variant — a tab strip swaps the body between named panes. Powers every catalog card and any frame needing several views.

Live demo — click the tabs to swap panes. The frame, padding, and border are inherited from .ds-doc-viewport; the variant adds the tab strip and the show/hide behaviour for panes.

A tabbed viewport keeps related views inside a single shared frame — same border, same padding, same controls slot — and swaps the body content from a tab strip across the top.

Switching is purely client-side; the inactive panes stay in the DOM (with hidden) so search, screen readers, and the Concise / Detailed toggle still find them.

Viewport reference

Anatomy, modifier matrix, and Do/Don't for the .ds-doc-viewport primitive — the canonical preview surface in the catalog.

Default surface — transparent. Sits directly on the page; padding only, no chrome. Add --tabbed for the tab-strip-needs-a-panel case, or --showcase for the brand-gradient backdrop. Non-tabbed previews (--stack, --row, --center, bare) inherit the transparent base so demos read against the page surface without a competing well.

badge

Showcase surface — ambient brand-gradient backdrop (mimics decks, proposal pages, hero surfaces). Reach for it when demoing how a material reads against a real-world brand-tinted background. The canonical case is glass: the gradient shows through the translucency, revealing the material’s actual character.

Paper
Solid card
Glass
Translucent + blur
Halo
Brand wash
Gradient
Brand fill

Layout, size, and height modifiers stack orthogonally — see the Anatomy tab for the full matrix.

Authoring

Asset cells

The figure-and-caption cells that render every library grid on this page, and the hover-revealed download button each asset carries. Catalog-meta (tier 1) — these classes build the asset libraries; never reach for them on product or marketing surfaces.

Asset cell .ds-doc-avatar-cell · .ds-doc-icon-cell

Figure-and-caption grid cell for the asset libraries; avatar, portrait, icon, and partner-logo cells reveal a live download button on hover.

Every cell below is live — hover (or tab) to reveal the download button; clicking saves the canonical file. Avatar and portrait cells resolve the file from the rendered <img> (largest srcset candidate); icon cells resolve it from data-icon. Partner-logo cells additionally reveal a mode-flip toggle (top-left) that previews the cell inline in the opposite mode — the download always saves whichever variant of the dark/light pair the cell shows.

ethereum
ethereum
activity
kim
kim

Three cell shapes, one button: a 40px avatar (SVG), a 24px Lucide icon (SVG), and a 96px portrait whose download resolves the 1024px master (PNG).