Why Tailwind v4 changes the game
Tailwind v4 pushes tokens towards native CSS via `@theme`. Gone is the verbose `tailwind.config.js` — in come real CSS variables, usable in both JS and CSS, overridable by media query, theme, or context.
Immediate benefit: a dark mode that no longer requires `dark:` on every class. You redefine the CSS variables inside `:root[data-theme="dark"]` and the whole design system switches cleanly.
Performance: Tailwind v4 generates less CSS than v3 for the same design. On the VALRY LABS codebase, the final CSS went from 32 kB to 18 kB gzip (-44%).
Tokens: the source of truth
Three-layer structure: raw tokens (base colours, scales, durations), semantic tokens (background, border, and accent colours per context), component tokens (variant X of the button).
Raw tokens are never used directly inside components. Everything goes through the semantic layer. That lets you change the base palette without touching a single component.
Semantic naming convention: `--color-bg-primary`, `--color-fg-primary`, `--color-border-strong`, `--color-accent`. Readable, unsurprising, dark-mode ready.
Storage: tokens in CSS (`globals.css`), mirrored in TypeScript (`tokens.ts`) for JS usage (canvas, dynamic charts). Both files are generated from a single `tokens.json` source via Style Dictionary.
Primitives: the atomic layer
Primitives = the reusable base bricks: Button, Card, Container, Heading, Badge, Overline. No business logic, only stylistic variants.
We use shadcn/ui as the base (Radix + Tailwind, copy-paste, no runtime dependency) then customise. It avoids reinventing the wheel on complex components (Dialog, Popover, Combobox) while keeping ownership of the code.
Rule: every primitive exposes its variants via `cva` (class-variance-authority). No ternaries in `className`, no concatenated strings. Composition is typed and IDE-toolable.
Native accessibility: Radix UI guarantees keyboard behaviour, ARIA, focus traps, and more. We never hand-roll a Combobox or a Dialog — we dress Radix.
Composites: the business layer
Composites = assemblies of primitives into functional units: `ServiceCard`, `TestimonialCard`, `HeroSection`, `CTASection`. This is where the brand's signature design lives.
A composite never reinvents a button: it consumes `<Button>` and passes the right `variant` / `size`. A composite's API is intentionally narrow — no primitive leakage.
On VALRY LABS, composites live in `components/sections/` (full page blocks) or `components/cards/` (business cards). Each has its Storybook demo.
Accessibility: non-negotiable
WCAG 2.2 AA is the minimum target. Contrasts validated by `@axe-core/playwright` in CI. No merge if a critical violation is detected.
Components: visible focus (`focus-visible:ring-2`), touch targets ≥ 44×44 px, `aria-label` on icon-only buttons, `aria-describedby` for form errors.
Animations: everything respects `prefers-reduced-motion`. Motion components have an instant fallback. Beauty never justifies exclusion.
Regular audits: Lighthouse a11y ≥ 95 mandatory, manual NVDA/VoiceOver review for every major feature.