Foundations
The raw material: color, type, spacing, radii, and shadows. Components live in the index, where each opens to its full documentation.
Colors
Layer 1 primitives mirror Figma variables 1:1. Layer 2 semantic roles alias from Layer 1 and are what components consume.
Brand
The brand accent — CTAs, focus rings, active nav, hover states. This is the single value a brand overrides on import; the swatch reads it live, so it's always the truth, not a typed copy. The sandbox's own identity is a sibling-neutral green; the deeper ink shade carries small green text on white so links stay legible.
Brand Ramp & Gradients
Seven tints of the primary, interpolated in OKLab, plus the primary's own ramp gradient. The signature brand sweep (primary → accent) and the accent's own ramp live in the Accent collection below — they need a second color to mean anything. Decorative: header / hero accents, highlights, sequential charts — they sit behind dark text or none, so no contrast obligation.
Accent (second brand color)
The opt-in second color — a real accent with its own button variant, brand tag/chip, ramp, and two gradients: its own accent ramp plus the signature brand sweep (primary → accent), which only exists once there's a second color. Generated by the setup tool's rules. The whole collection is gated behind --brand-accent-enabled, so one-color brands never see it; a two-color brand turns it on in Brand setup.
This brand runs a single color, so the accent family is off (--brand-accent-enabled: 0). Add a usable second color in Brand setup to turn it on — the accent button, brand tag, ramp, and gradient populate automatically.
Mesh Gradient Images (pre-rendered)
Pre-rendered mesh-gradient IMAGE assets (raster PNGs, inlined with the package) — used for avatar backgrounds and reference-page header bands. These are fixed images, not applicable CSS gradients. Grouped by color family.
Surface
Application backgrounds and container fills.
Neutral Scale
Primary text + UI grayscale. Seven tiers from lightest to darkest, read live from the tokens.
Grey Scale
Alternative mid-greys distinct from the neutral family.
Status · Default
Inactive / neutral state for status tags and chips.
Status · Hue Families
Seven semantic hues. Dark = saturated/bold variant, base = primary shade, light = background tint.
Bulb Palette
Product colors for Christmas light bulb rendering. Domain-specific.
Wire Palette
Product colors for wire color selection.
Semantic Role Tokens (Layer 2)
shadcn-compatible tokens consumed by components. Each aliases to a Layer 1 primitive — read live, so primary and ring track the brand accent automatically.
Typography
App-specific scale. Max 20px, min 10px. Default body 13–14px. Tight tracking and dense line-heights keep layouts compact and utility-focused. Landing page typography is defined separately and does not share this scale.
Font stacks
Loaded via next/font in src/app/(frontend)/layout.tsx.
--font-sansGeist — default body & UI text--font-monoGeist Mono — code, tokens, measurements--font-eyebrowMagdaClean — display / eyebrow accentsHeadings (14–20px)
Page title at 20px (max). Drop to 18 / 16 / 14 for nested hierarchy. Tight negative tracking at larger sizes.
Body (12–14px)
Default body sits between 13 and 14. 12 is reserved for secondary / metadata text.
Labels & captions (10–11px)
Uppercase eyebrow for section headers. Small field labels. Metadata/captions at the floor of the scale.
Monospace (10–11px)
Geist Mono for token names, hex values, code snippets, and measurements.
Radii
Scale from 4px to 26px. Use Tailwind classes: rounded-xs, rounded-sm, rounded-md, rounded-lg, rounded-xl, rounded-2xl, rounded-3xl, rounded-4xl, rounded-full.
Scale
Visual comparison of all radius tokens.
Common applications
Where each radius is typically applied.
- radius-xs · tiny elements, inline tags
- radius-sm · chips, badges, small tags, menu rows
- radius-md · inputs, form controls
- radius-lg · default (buttons, cards, popovers)
- radius-xl · container cards, modal content
- radius-2xl · list page wrappers, elevated panels
- radius-3xl/4xl · hero sections, marketing surfaces
- radius-full · pill shapes (switches, avatars, fully-rounded buttons)
Spacing & Rhythm
Two scales, kept separate. An 8px grid for the space BETWEEN things, and a touch-target scale (24 / 32 / 40) for how tall the things you TAP are. This supersedes the old Brite Design System dashboard scale (4-multiples capped at 24), which is folded in here, conformed.
The core idea — two scales
The mistake to avoid is sizing a control off the spacing scale, or spacing things off the target scale. They do different jobs.
- Layout spacing — the empty room between things (gaps, padding, margins). Uses the 8px grid.
- Touch-target size — how tall a control is (button, field, row). Uses the 24 / 32 / 40 tiers (also on the 8px grid).
- What the grid governs: spacing and element heights. Not widths of fluid elements (cards, containers float with the layout), not type sizes, not radii — those run on their own scales.
Layout spacing — the 8px grid
Every gap, padding, and margin is a multiple of 8 from this scale (the --ws-space-* tokens). No 5, 10, 28, 36. A 4px half-step is allowed only for tiny optical nudges, with a comment.
Touch targets — 24 / 32 / 40
How tall anything you click, tap, or type into is. Sized by CONSEQUENCE, not frequency: reversible / in-a-list → secondary; navigates / submits / hard-to-undo → primary. The tinted box is the tap area; the glyph inside can stay small (hit-area ≠ glyph).
- Hit-area ≠ glyph: a 16px icon lives in a 32px padded button. Size the tappable box to the tier; the glyph stays its natural size.
- Spacing between adjacent targets: ≥ 8px.
- Uniform across breakpoints: a control is the same size on mobile, tablet, and desktop. The one exception is dense data tables (below).
Vertical rhythm — proximity
Distance encodes hierarchy. Each step OUT in the structure jumps one notch up the 8px scale.
16–24 — one footer column to the next, a label group to its input group.32–48 — masthead → links → utility bar; hero → content.Page gutter
The horizontal safe margin so nothing kisses the screen edge. Ramps on the --ws-section-px scale.
Special cases
The handful of places that don't take the plain tier rule.
Breakpoints
Where layout reflows.
- Mobile < 768 · Tablet 768–1023 · Desktop 1024–1279 · Large ≥ 1280
- At each step: nav reflows (rail ↔ drawer below 768), column counts drop (4→2 at 1024, 2→1 at 768), dense tables → cards below 768, type scales per the ramp.
Rules of thumb
Quick checks for anything new you build.
- Spacing not a multiple of 8? It needs a reason you could say out loud (optical nudge, fixed asset, hairline) — otherwise snap it.
- Anything tappable gets a tier: 40 if it's the main/consequential action or a field, 32 if it's navigational or in a dense list, 24 only for incidental.
- Element heights snap to the grid; widths float, type and radii have their own scales — don't force those onto 8s.
- Same control = same size at every breakpoint. Only dense data tables relax on desktop (and reflow to cards on mobile).
- Make the tap area the tier, not the glyph — pad small icons up rather than growing them.
Layout Grid
A responsive column system for arranging a page, a dashboard, or a form. It reflows by its container, and it comes in two modes for the two jobs it actually does. It is a pattern to build around when it helps, not a container everything must use. 12 columns because 12 splits cleanly into halves, thirds, quarters, and sixths.
Two modes, one grid
Pick the mode by the job. The rule is short: cards use Dashboard, pages use Content.
Dense cards. Stacks 1-up on a phone, 2-up on a tablet, opens to the full 12 on desktop. Use it for KPI tiles, lists, charts, and the asymmetric 9/3 card layouts.
Full pages. Holds 12 columns at every size; only the columns resize, the count never changes. Use it for marketing and editorial layouts. On a phone the 12 columns get narrow, so reach for this when that is what you want.
Both mirror grids already shipping across Brite, so the system reflects how layouts are really built. Flip between them live in the Responsive Preview tool, at any device width.
Dashboard mode: the canonical card rows
On desktop the 12 columns carry a small set of canonical, asymmetric rows (they each sum to 12). 6+6 is deliberately avoided: symmetric splits read as two things fighting for attention. Prefer the 9/3 with a primary lane and a sidebar lane. On smaller screens these restack to 2-up, then 1-up.
Content mode: 12 columns that only resize
The same split holds its shape at every size; only the column width changes. Here is an 8 / 4 content + rail split shown wide, then narrow, to show the count never drops.
Spec & tokens
A few shared values define the grid; change a token in one place and every instance follows.
| Mode | Phone | Tablet | Desktop | Gap |
|---|---|---|---|---|
| Dashboard | 1 col | 2 col | 12 col | 12px |
| Content | 12 col | 12 col | 12 col | 16 / 24 / 32px |
--layout-grid-gutter-sm / -md / -lg16 / 24 / 32px content gutter, stepped by width--layout-grid-gutter-dense12px dashboard card gap--layout-grid-max1200px; the grid centers within this widthThe gutters are grounded in the shared spacing scale (--ws-space-sm / -md / -lg). The breakpoints are the Tailwind v4 container sizes the grid reads from its own width: tablet at 672px, desktop at 1024px. Outer page margins are deliberately not the grid’s job: a section wrapper sets the edge padding with the shared --ws-section-px tokens (16 / 32 / 48px), while the grid stays focused on columns and centers within --layout-grid-max.
Reference, not a requirement. The grid is here to align to when it helps a page hold together. A layout is free to step outside it when the content calls for something bespoke. Build around it; don’t let it box you in.
Shadows & Effects
Composite tokens that combine color, blur, spread, and offset, applied through the component state machines (buttons, switches).
Button shadow tokens
Applied via .btn-default, .btn-primary, and .btn-fx-secondary state machines.
Gradient overlay
A faint warm-white highlight layered over the brand primary via color-dodge — the soft gloss on the primary button's interactive states. The fill is the brand primary (currently the sandbox green); the highlight is a near-cream white.
Switch glow
The toggle's own on-state effect — a brand-tinted glow built just for switches, separate from the button tokens above. Derived from --brand-primary so it stays on-palette and recolors with the brand.
Light-switch glow
The BriteLightSwitch on-state 'rim glow' — an outer halo, a bright inset rim, and a broad inner bloom over a brand-tinted near-black track. Built from the switch hue tokens so it recolors with the brand. Geometry is the lab reference (a 168px track); the component scales it per size.
Disabled pattern
45° diagonal stripes at ~3px pitch. Applied as repeating-linear-gradient.
Accessibility
The floor beneath every other foundation. Rebrand the spacing, color, type, and radii freely — but never cross these lines. They are the minimums that keep the interface usable for people on touch screens, with low vision, with color blindness, or who feel motion strongly. Every rule here is a ratified standard, not a preference.
Read this first
How to treat this section when you customize the system.
- The other foundation pages describe defaults you may change. This page describes limits you may not.
- If a brand decision and an accessibility rule collide, the accessibility rule wins. Find another way to express the brand.
- These are floors, not targets — clearing them comfortably is encouraged. Sitting exactly on the line is the minimum, not the goal.
Touch targets — 24 / 32 / 40, by consequence
Interactive controls come in three sizes, chosen by how consequential the action is — floor, secondary, primary. Critically: the size is chosen by consequence, NOT by device. The same tiers apply on desktop and on a phone; the system never resizes a control just because it detects a small screen.
The one exception: true data tables may run at desktop density and reflow to stacked cards below 768px. Nothing else resizes by device.
The 24px hit-area floor — “touch padding”
No tappable control may have a hit area smaller than 24×24px — ever. When a control needs to LOOK small (a 16px checkbox, a slider thumb, a chip's remove-✕), you do not shrink the tap zone. You keep the small look and pad the tappable area out to 24px. The padding is invisible; the safe target is real. This is what makes a tiny control reliably tappable with a thumb.
Controls that look small but must still clear 24px:
- Checkbox & radio — small ~16px glyph; the clickable label-row is padded up to its 32px tier
- Slider thumb & switch — small visual, 24px grab zone
- Filter-chip / tag remove-✕, tab action triggers
- Number-input steppers, collapse carets, resize handles
Rule of thumb: if you can tap it, measure the tappable box — not the ink. If that box is under 24px, add padding until it isn't.
Color contrast — 4.5:1 and 3:1
When a brand recolors the system, these ratios are the line. Text or a control that fails them disappears for low-vision users — and our automated accessibility gates reject it. Check every new color pairing against its background.
Visible focus — always, and unstyleable-away
Anyone navigating by keyboard needs to see where they are. Every interactive element keeps a visible focus ring; it may be restyled to match the brand, but it may never be removed.
- Standardize on the full-opacity focus token (
--ring/--focus-ring). Neveroutline: nonewithout a replacement. - Provide a forced-colors fallback: under Windows High-Contrast mode the brand ring may vanish, so a system
outlinemust take over. - The focus indicator must itself meet 3:1 contrast against what's behind it.
Respect “reduce motion”
Some people get nauseated or disoriented by movement. When the operating system reports a reduced-motion preference, honor it.
- A global
@media (prefers-reduced-motion: reduce)block strips non-essential transitions and animation to near-instant. - Essential motion that conveys meaning (a loading state) may remain, but gently — no large parallax, bounce, or auto-playing movement.
- Never gate a motion preference behind an in-app toggle only; read the OS setting.
Operability — tracked separately (not yet documented here)
This page covers the VISUAL accessibility floor — what a control looks and feels like. A second, equally important question is whether you can OPERATE it without a mouse.
- Full keyboard navigation, screen-reader announcements, and accessible names for icon-only controls live in the operability track, which has not been completed yet.
- When that work lands, its standards will join this page. Until then, treat keyboard + screen-reader support as required-but-undocumented here, and don't assume the visual floors above are the whole of accessibility.