Reference

Design System

The visual language, semantic tokens, geometry, depth, typography, and motion of 4AF.

4AF uses warm-neutral greys, a single amber accent, compact controls, and short, physical motion. Fill and elevation show whether something is sunk into the page, anchored to it, or floating above it.

Color palette and roles

Components consume semantic roles by default. Direct palette ramps are reserved for status accents and fixed neutral control states. A consuming project can reskin the semantic roles; it must also remap the relevant ramps to change those exceptions.

RoleLight treatmentDark treatmentPurpose
BackgroundWarm near-white (oklch(0.985 0.002 70))Neutral near-black (oklch(0.1448 0 0))Page ground and sunk wells
SurfaceWhite (oklch(1 0 0))Soft black (oklch(0.178 0 0))Chrome and anchored popups
Raised surfaceWhite (oklch(1 0 0))Deep black (oklch(0.130 0 0))Dialogs, sheets, and palettes
ForegroundWarm near-black (oklch(0.155 0.006 70))Warm near-white (oklch(0.985 0.002 70))Primary text and neutral actions
Focus ringAmber (oklch(0.702 0.176 52))The same amberFocus and deliberate emphasis
DestructiveVivid red (oklch(0.6 0.225 26))The same redIrreversible and failed actions
SuccessDeep green (oklch(0.52 0.13 152))The same greenConfirmed outcomes only

Primary is ink, not colour. Amber is used for focus and rare emphasis; destructive red and success green appear only when they mean something. Quiet destructive actions, such as menu and toolbar rows, use red text without a red hover fill.

The destructive fill deliberately favors a brighter red with white text. This pairing falls below the 4.5:1 contrast target for normal text; OKLCH lightness alone is not a contrast check.

A component's fill communicates its layer. background is the page and sunk wells such as Input, Checkbox, Radio, and Sidebar Inset. surface is chrome and anchored popups such as Sidebar, Dropdown Menu, Popover, and Hover Card. surface-raised belongs to elements floating over everything: Dialog, Alert Dialog, Sheet, and Command Palette. A consuming project decides what those semantic roles resolve to.

Typography

Geist Sans is the interface voice: compact, neutral, and highly legible. Geist Mono is reserved for code, command examples, keyboard hints, and small technical labels.

The scale: 11px technical labels, 12px compact text, 13px default UI, 15px body copy, then 18px, 22px, 28px, and 36px headings. Larger sizes tighten their leading and tracking; body copy gets more. Numbers that update in place use tabular figures so the text around them doesn't shift.

Geometry and shape

Radii range from 3px to 28px. Nested shapes are concentric: the outer radius equals the inner radius plus the padding between them, so a 14px shell with 4px of padding holds a 10px child.

Controls are compact by default. Icons remain geometrically centered inside their boxes; leading and trailing icons beside text receive optical spacing through component attributes rather than one-off margins.

Button controls share a 24px/30px/34px/38px scale for xs/sm/md/lg; component-specific objects such as badges, checkboxes, radios, sliders, and table density keep their own geometry. Their corner radii follow two tiers: 6px for xs/sm and 8px for md/lg. Labeled button content uses an 8px icon-label gap at xs/sm, 10px at md, and 12px at lg; leading and trailing icons receive a small optical edge adjustment. Checkbox is fixed at 16px with a 4px radius. Other rectangular controls follow the same tiers: 6px for compact controls and 8px for form rows. Circular, pill-shaped, status, and list-row elements keep their own geometry.

Optical alignment

Center icon-only controls by their SVG box. If a glyph appears off-center, inspect its ink with getBBox() before adding an offset: a glyph may already be optically balanced within the box, so a blanket shift can misalign it. Correct a custom glyph inside its SVG rather than moving every icon-only button.

Beside text, leading and trailing icons use data-icon-start / data-icon-end to reduce the padding on their side by 2px. Keep this text-button spacing separate from icon-only centering.

Depth and elevation

Buttons and cards use shadow-border: a translucent hairline ring, a close lift, and a soft ambient shadow, which works over any background. Raised and overlay surfaces add more separation.

In dark mode, most drop shadows are replaced by faint light rings, since shadows barely show on near-black. Real borders are kept for dividers and table cells.

Component styling

  • Buttons use neutral ink for the primary action, with optical icon spacing and press feedback. Joined buttons keep their outer box still while pressed, so seams don't open.
  • Fields sit on the background layer and strengthen their ring on focus. Invalid state is exposed to assistive technology, not only shown in red.
  • Anchored panels use the surface layer and enter with a scale, blur, and fade from the trigger.
  • Overlays use the raised layer and stronger elevation, with behaviour and focus management from Base UI.
  • Status colours are shared by alerts, badges, and action outcomes, so a state looks the same as a sentence or a label.

Layout principles

Spacing is compact. Related controls sit in close groups, and sections are separated more clearly than individual fields. Page shells keep navigation fixed and let the content region scroll. Popups open next to the control that opened them.

Motion language

CSS handles hover, focus, ordinary press feedback, and simple entrances and exits. Button and Toggle use Motion for spring presses. Motion also handles retained exits such as Morph's outgoing content, gestures, springs, and layout animation. Transitions name the exact properties they change and prefer compositor-friendly motion; Collapsible and Sidebar have documented layout-motion exceptions.

Four CSS durations, fast, base, slow, and slower, cover focus feedback through sheets. JavaScript duration and easing defaults mirror the CSS tokens and must be updated together; runtime CSS overrides do not change JavaScript animations. Under reduced motion, CSS transitions collapse to 1ms so lifecycle events still fire, and Motion transitions run with zero duration. Looping loaders keep their own speed rather than collapsing into a strobe.