Library · Primitives
Controls & surfaces
The whole vocabulary, with the class string printed beside each piece. Everything here is server-rendered — no control on this page needs client JavaScript to look right. Copy the string, keep the tokens, and the primitive lands correctly in any repo running the same four variables.
Action
Buttons
one primary per view
primary inline-flex items-center gap-2 rounded-lg border border-border bg-foreground px-4 py-2.5 text-sm font-medium text-background transition-opacity hover:opacity-90 secondary inline-flex items-center gap-2 rounded-lg border border-border bg-background px-4 py-2.5 text-sm font-medium text-foreground transition-colors hover:bg-muted/40 quiet text-[13px] font-light text-muted-foreground/70 transition-colors hover:text-foreground
Sizes
py-1.5 / py-2.5 / py-3
sm rounded-md px-3 py-1.5 text-[12px] md rounded-lg px-4 py-2.5 text-sm ← default lg rounded-lg px-5 py-3 text-[15px]
Links
underline offset 3px, border-colored rule
body underline decoration-border underline-offset-[3px] transition-colors hover:text-foreground hover:decoration-foreground/50 accent text-primary underline decoration-border underline-offset-[3px] nav text-muted-foreground transition-colors hover:text-foreground
Surface
Cards
card/40 over background, never pure black-on-black
Base card
The default container for grouped content.
Inset callout
For the closing thought, the caveat, the aside.
base rounded-xl border border-border bg-card/40 p-5 inset rounded-xl border border-border/80 bg-muted/15 px-5 py-4 ← callout / closing note media overflow-hidden rounded-lg border border-border/80 bg-muted/10 hover transition-colors hover:bg-muted/25
Chips & tags
uppercase, tracked, never colored by category
chip rounded-md border border-border/60 px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-[0.12em] text-muted-foreground/70 tech inline-flex items-center rounded-md border border-border/70 bg-muted/30 px-2 py-0.5 text-[10.5px] font-medium uppercase tracking-[0.14em] text-muted-foreground
Status
the one place a hue other than the accent may appear
neutral border-border/60 text-muted-foreground/70 active text-primary warn text-[oklch(0.72_0.13_75)] danger text-[oklch(0.62_0.2_25)]
Input
Text fields
label above, hint below, never placeholder-as-label
We reply within 48h.
Message
Search
label text-[10px] font-medium uppercase tracking-[0.14em] text-muted-foreground/60 input w-full rounded-lg border border-border bg-background px-3 py-2.5 text-[13px] text-foreground placeholder:text-muted-foreground/40 focus:border-foreground/30 focus:outline-none hint mt-1.5 text-[11px] font-light text-muted-foreground/60
Choice
square = many, round = one
box h-4 w-4 rounded border border-border bg-background checked h-4 w-4 rounded border border-foreground/40 bg-foreground text-background radio h-4 w-4 rounded-full border border-border toggle h-5 w-9 rounded-full border border-border bg-muted/40 → bg-foreground when on
Tabs
active carries the accent; the rest stay quiet
active text-primary inactive text-muted-foreground/50 hover:text-foreground wrapper flex flex-wrap items-center gap-x-1 gap-y-1 -ml-2.5
Content
Code
inline chips and blocks share the muted ground
Run pnpm dev and open the app.
npx shadcn@latest add https://www.hitlkit.dev/r/hitl-card.json → writes components/hitl/hitl-card.tsx
inline rounded bg-muted/60 px-1 py-0.5 font-mono text-[12px] block overflow-x-auto rounded-lg border border-border/80 bg-muted/30 p-4 font-mono text-[11px] leading-relaxed text-foreground/80
Lists
em-dash markers, never bullets, for prose lists
- Watch the work first. research before design
- Prototype in code. working surfaces over mockups
quiet li flex gap-3 → <span className="mt-[7px] h-1 w-1 rounded-full bg-muted-foreground/40" /> disc list-disc space-y-2 pl-5 marker:text-muted-foreground/50 lead <span className="text-foreground/85">Term.</span> then body
Table
rules only between rows; no zebra, no vertical rules
| token | value |
|---|---|
| --background | oklch(0.145 0.004 106) |
| --foreground | oklch(0.93 0.003 106) |
head border-b border-border + label class row border-b border-border/40 cell py-2 pr-4 align-top text-[12px] font-light
Media frame
every screenshot gets the same frame + caption
A caption states what the frame shows, not that it is a screenshot.
frame overflow-hidden rounded-lg border border-border/80 bg-muted/10 caption mt-1.5 text-[11px] font-light text-muted-foreground/70
Section header
the page rhythm — kicker, title, standfirst
Section kicker
The section title
A standfirst that says what this section argues, in one sentence.
kicker text-[11px] font-medium uppercase tracking-[0.18em] text-muted-foreground/70 title text-xl font-light tracking-tight text-foreground/90 stand mt-2 max-w-xl text-[13px] font-light leading-relaxed text-muted-foreground
The measurable half — foundations — carries the spacing scale, radii, motion timings, and breakpoints these primitives are built on. Take both and the system travels intact.