Skip to content

Components

The shared primitives in src/components/ui/, re-exported from @/components/ui. Import from there — one canonical path per primitive. Every primitive consumes tokens only.

Button — button.tsx

cva-based. Variants: default (foreground/background), subtle, ghost, ember (primary action), outline, danger. Sizes: sm (h-7), default (h-9), lg (h-10), icon (h-9 w-9). Always includes focus-ring; transitions colors only. Consumes --foreground, --background, --subtle, --ember, --ember-foreground, --border, --danger.

Badge — badge.tsx

Small rounded pill. Default is bg-subtle/text-foreground. Pass a color (hex, usually a DB label/status/project color) to tint background at ~12% alpha + a leading dot. Note: the design spec showed named tones — the real component is intentionally color-driven so DB-assigned label colors flow through.

Card — card.tsx

Card + CardHeader/CardTitle/CardDescription/CardContent/CardFooter. Variants: default (border + bg-card), subtle (border + bg-card/40, translucent — the default export), outlined (border only, for nested cards).

EmptyState — empty-state.tsx

Variants page / section / card (decreasing icon + type scale). Optional icon, title, description, action; as="li" to slot into list cards.

Input — input.tsx

Token-bordered text field; focus-ring for the ember focus halo.

Kbd / Chord — kbd.tsx

Kbd renders a single key cap (.kbd); Chord composes a sequence (e.g. g then d). Hardcoded small — a true label, not body content.

Section / SectionHeader / SectionDivider — section.tsx

Heading row (small semibold title + muted hint + right actions) over a body. SectionDivider is the M9 hairline — a 1px border line that runs a slow ember sweep when motion is on (static otherwise). Marginless; let the surrounding space-y-* stack space it.

Skeleton family — skeleton.tsx

Skeleton, SkeletonText, SkeletonRow, SkeletonList, SkeletonCard. All built on .ui-shimmer (reduced-motion aware).

Spinner — spinner.tsx

Token-colored loading spinner; sizes sm/md/lg.

Toast — toast.tsx

ToastProvider + toast() + useToast(). Variants render a left stripe per tone: default = ember, success, warning, error = danger.

Typed primitives above the legacy Dialog: Confirm, QuickForm, SidePanel, Picker, Drawer, plus useModalBehavior and draft helpers (saveDraft/readDraft/clearDraft). Prefer these over raw <Dialog>.

Avatar — avatar.tsx

Initials/image avatar; sized in px. Used in assignee chips and pickers.

AgentPresenceDot — agent-presence-dot.tsx

Status dot: ONLINE → --success, BUSY → --warning, OFFLINE → --muted-foreground. pulse adds the M10 ember/success "breath" — ONLINE only; BUSY/OFFLINE stay flat.

Tooltip — ui/tooltip.tsx + global delegate

Tooltip wraps a child and shows a themed floating label. The app also mounts a global NativeTooltips delegate that intercepts every title attribute app-wide — it suppresses the browser's native tooltip and renders the themed one instead (restoring title at rest for accessibility). Net effect: no browser tooltips anywhere. New code can use either a title or <Tooltip>.

CountUp / useCountUp — lib/use-count-up.ts

The M6 metric tween: counts 0 → value once on scroll-into-view (rAF, reduced-motion aware, once-per-mount).

Axiom-Labs · built for humans and agents