- Revised: 2026-09-17 by #16 — the primitives barrel ships Effect without
optimizePackageImports; the theme is applied before first paint by a script - Revised: 2026-09-23 by ADR 0011 — a first visit paints the default theme, not
prefers-color-scheme; the theme control is the site's own menu
Context
The old app styled with Emotion, which generates CSS at runtime from JavaScript: every styled component must be a client component, which works against pages rendered at build time (ADR 0003). entifix ships a design system built for Tailwind v4.
Decision
- Tailwind v4, CSS-first, with
@entifix/style/tokens.cssand its palette presets, each scoped to[data-theme]on<html>. The site's identity lives in the token values, never in a component. - Layout and atoms come from
@entifix/react-controls/primitives: Box, Center, Cluster, Cover, Grid, Sidebar, Switcher, Stack, atoms, Card, ThemeProvider and ThemeSwitcher. - ⚠️ The main barrel of
@entifix/react-controlsis never imported, nor./preferences: the barrel pulls in the entity table and query machinery, about 541 KB, and./preferencesbrings Effect into the browser. - ⚠️
./primitivesgoes throughoptimizePackageImports, because it is a barrel too and entifix's packages declare nosideEffects. Imported as-is, oneStackputs every'use client'module of the barrel on the page,ThemeSwitcher's@entifix/coreimport with them, and Effect reaches the browser (measured: ~300 KB of gzipped JavaScript on a placeholder page instead of ~200 KB).experimental.optimizePackageImportsfor@entifix/react-controlsand@entifix/coreinnext.config.jsis what keeps a page to the modules it renders. - The theme is applied before first paint by an inline script that reads
the stored choice, or else the site's default theme
(ADR 0011). entifix's
ThemeProviderwrites its starting theme to<html>on mount, so it starts from the theme already painted, and the theme control renders only after hydration. - ⚠️ Tailwind v4 does not scan
node_modules: an@sourcefor the primitives'dist, or their classes produce no CSS and nothing reports it. - Fonts self-hosted with
next/font, so they are present when a page prints. - d3 visualizations are client components coloured from the theme's CSS variables.
Alternatives
- Emotion — runtime CSS, client components only.
- CSS Modules — zero runtime, but no tokens, and nothing of entifix exercised.