LifeDS

Wellzesta

Design System

Foundations, principles and usage rules for the design system — the reasoning behind the decisions, not just a component catalog.

Tailwind v4Base UI@tabler/icons-reactOKLCHWCAG AA

Token architecture

Three layers mirror how variables are organized in Figma. Design and code share the same names.

Primitive

--teal-700: oklch(0.511 0.096 186.391)

Raw scale values (teal · red · slate · base neutrals). Never referenced directly by components.

Semantic

--primary: var(--teal-700)

Named roles that carry meaning. Swap light/dark mode, or the whole brand, without touching a component.

Component

bg-primary text-primary-foreground

Components only ever read semantic tokens through Tailwind utilities — never a primitive, never a raw value.

Design principles

Why the system is built the way it is — not just what to do, but the reasoning behind it.

Non-linear scales where perception isn't linear

The radius scale uses a 0.2 step below lg (sm·md·lg) and a 0.4 step above it (lg·xl·2xl·3xl·4xl). Small corners need subtle differences to be perceptible; large corners need bigger jumps. It's not an arbitrary number — it's proportional to how the eye perceives curvature.

OKLCH instead of HEX/RGB

The entire primitive palette (teal, red, slate) is defined in OKLCH, not hex. OKLCH separates lightness, chroma and hue in a perceptually uniform way — varying only lightness across a scale's steps (50→950) produces predictable, practical contrast, which HEX/RGB doesn't guarantee.

WCAG AA is a floor, not a ceiling

Every text/background pair in the system is audited for contrast (minimum 4.5:1 normal text, 3:1 UI components). We've already fixed real inconsistencies found in that audit — e.g. destructive-foreground in dark mode, focus ring opacity — and the process is repeatable for every new token.

Consistent naming across components

Every component with a size scale uses the same names: sm · default · lg · xl (plus xs/icon-* where it makes sense, like in Button). This was standardized after auditing the system and finding Input/InputGroup/Combobox using "md" while everything else used "default" — same height, different name. Fixed.

Usage rules

Apply to every component under components/ui/**.

Do

  • Reference only semantic tokens in components — never a primitive or a raw value (bg-primary, not bg-[#0f766e])
  • Icons exclusively from @tabler/icons-react — no other family in the project
  • Use cn() to merge classes, allowing consumers to override
  • Select for styled menus; Native Select when native browser behavior/performance matters more than visual customization
  • Combobox when the user needs to search/filter across many options

Don't

  • Hex or rgb() color directly in a component (bg-[#...], style={{ color: '#...' }})
  • Inventing a new size name outside sm · default · lg · xl
  • Icons from another library (lucide, heroicons, etc.)
  • Reimplementing a component that already exists in the system instead of composing from it
  • Raw black shadow/overlay (bg-black/10) — use bg-foreground/N to stay theme-aware

Foundations

Explore each foundation in detail.