Colors
Base — Warm surface
Base — Cool surface
Neutrals — Text & dividers
Brand — Copper
Semantic — Info (Blue)
Status
Typography
Space Grotesk — Headings
Documents in, data out.
Configurable extraction
Define the template once
Feed many documents through it
Data lands where you need it
IBM Plex Sans — Body
Sem Digitar turns receipts, invoices, and contracts into structured rows in a spreadsheet. Define the template once, then feed hundreds of documents through it.
Claude reads the document, applies your template, and writes the extracted fields to Google Sheets, an email, or a webhook. No manual typing.
Each field carries a name, a type, and a rule — the same ones you'd teach a new intern.
Sample · caption · helper text · meta annotation · footnote
Geist Mono — Numerics & code
1,248 documents · 99.2%
{invoice_number} · {total_amount} · {issued_at}
Spacing & Layout
Standards & overrides. This system adopts the mainstream layout rules (4pt grid, fixed type scale, three named max-widths, layout primitives over ad-hoc flex, Grid only for two-axis alignment). It overrides the reference standard's specific numbers in four places: spacing scale skips
20 / 40 / 80 / 128; body type is 15px (Linear/Stripe-style) rather than 16/17px; radii are subtle (6 / 8 / 10) rather than dramatic; container widths are device-derived (sm 640 / md 768 / lg 1024 / xl 1100) rather than role-derived. Treat these as authoritative — consuming projects follow this system, not the reference standard, when the two disagree.Spacing scale (4px base)
sp-1--sp-1
4px
sp-2--sp-2
8px
sp-3--sp-3
12px
sp-4--sp-4
16px
sp-6--sp-6
24px
sp-8--sp-8
32px
sp-12--sp-12
48px
sp-16--sp-16
64px
sp-24--sp-24
96px
Border radius
r-2--r-2 · Tight
6px
r-3--r-3 · Default
8px
r-4--r-4 · Card
10px
r-full--r-full · Pill
full
Container widths
smReading width
640px
mdForm · dialog
768px
lgApp content
1024px
xlDesign doc · marketing
1100px
Card padding
Three reusable insets. Every card, dialog body, and surface panel reaches for one of these — never an ad-hoc literal. Reach for
tight in dense rows or compact lists, default for standard cards / dialogs, lg for hero frames.tight--pad-card-tight · sp-4
16px
default--pad-card-default · sp-6
24px
lg--pad-card-lg · sp-8
32px
Page-shell side padding
One token, every viewport.
--pad-x: clamp(20px, 5vw, 32px). Use on <main>, marketing shells, and any page-level container — padding fluidly grows with the viewport from 20px on phones to 32px above 640px. Replaces ad-hoc padding: 0 32px + media-query overrides.Breakpoints
Three steps.
--bp-sm: 640px · --bp-md: 768px · --bp-lg: 1024px. CSS custom props can't appear inside @media queries (spec limitation), so the same literals are repeated in media rules. The tokens exist so JS, container queries, and clamp() math share a single source of truth.Container roles
Pick the role, not the size.
sm reading-width prose · md form / dialog · lg app content · xl design doc / marketing. Build pages from the .center-* primitives below — they apply both the right max-width and --pad-x.Layout primitives
Compose pages from these — don't re-derive flex per section.Each primitive owns its own gap and breakpoint logic, so spacing compounds consistently across section boundaries. Reach for raw
display: flex/grid only when none of these fits.Catalog.
.stack-{1,2,3,4,6,8}— vertical flow with token gap. The default for almost everything..cluster— horizontal wrap (tags, action rows, nav)..center / .center-prose / .center-form / .center-wide— max-width + horizontal centering +--pad-x..sidebar— fixed-width rail + flexible main (app shells)..switcher— flex-row above ~320px child width, flex-column below (card rows).
.stack-3 — gap: 12pxchild 2child 3.switcher · row above 320px child, column belowchild 2child 3Components & Icons
Buttons
Input
Badges
SuccessErrorWarningInfo
Chips
Badge vs Chip vs Button. Badge is a read-only status marker (Success / Error). Chip is a selectable taxonomy token — filter, tag, option — that toggles pressed state. Button triggers a one-shot action. If it's interactive and toggles, it's a chip; if it fires once, it's a button; if it only describes state, it's a badge.
Toggle chip vs Removable chip. Toggle chips use
aria-pressed — click flips state, chip stays visible (filters, multi-select). Removable chips use .chip-removable with aria-label="Remove [item]" — the whole chip is a one-shot button; click anywhere removes it. Do NOT set aria-pressed on a removable chip: it is not a toggle, and screen readers should not announce a pressed state for an action that only disappears.Card
Invoice template
Extracts supplier, total, issue date, and line items from Brazilian NF-e invoices.
Receipt template
Reads retail receipts and pulls merchant, amount, category, and timestamp.
Segmented control
When to use. Mutually-exclusive short list (2–5 items), all options visible at once, user picks one value. Billing cycle (monthly / annual), density (comfortable / compact), role (owner / admin / viewer). If the list is longer than 5, use a
<select>. If selecting a tab switches a content panel, use tabs instead — this pattern sets a value, not a view.Semantics. Container is
role="radiogroup" with aria-label; each button is role="radio" + aria-checked="true|false". Arrow keys (←/→, ↑/↓) move focus and selection; only the active button is tab-stoppable.Dialog & AlertDialog
Dialog vs AlertDialog.
role="dialog" is for anything modal: forms, info, onboarding flows. role="alertdialog" is reserved for destructive / interrupting confirms — screen readers announce it more forcefully. Rule of thumb: if ignoring the prompt has a cost, it's an AlertDialog. Otherwise it's a Dialog.Build with native
<dialog>. Use the HTML <dialog> element with .showModal(). You get focus-trap, escape-to-close, ::backdrop, and correct default ARIA for free.Action-button layout.Desktop: right-aligned, [cancel · confirm]. Mobile (< 480px): reversed-stack, confirm on top for thumb-reach. Primary button verb matches the action — "Delete", "Send invites", "Cancel subscription". Never "OK" / "Yes".
Destructive actions
Tier the treatment to the blast radius. Not every destructive action needs a modal. Match the friction to the cost of a mistake. This system does not ship a red "danger" button variant — copy, context, and type-to-confirm carry the signal.
Reversible
Archive, hide, soft-delete. No modal. Show an undo toast for 5–10s in an
aria-live="polite" region. Gmail archive, Notion move-to-trash, Linear archive issue.Recoverable
Move to trash with a retention window (e.g. 30 days). Use
<dialog role="alertdialog">. Neutral primary button; destructive intent lives in the copy. "Move to trash? We'll keep it for 30 days."Irreversible
Hard delete, revoke API key, cancel subscription, destroy workspace. AlertDialog + type-to-confirm. Confirm button stays disabled until the user types the exact target name.
Loaders
Two loaders, two contexts. Full-page loader when a whole surface or large region is waiting on one operation. Inline loader when the wait is local — inside a card, chat bubble, button action, or a step within a form. Never show both in the same viewport at once.
Loading…
Loading…
Skeletons
Preview the shape of what's coming. Skeletons are not loaders — they are placeholder blocks sized to match the content that will render, shown while that content is being fetched.
Icons
check
x
plus
arrow-right
arrow-up
search
info
alert
document
upload
external-link
settings