Skip to content

ADR-0018: Design System Architecture — Tokens, Themes, and Tooling

  • Status: accepted
  • Deciders: Reasonix agent (2026-06-27)
  • Date: 2026-06-27
  • Issue: #279

Context

Neemias uses Tailwind CSS 4.1 configured via CSS-first @theme, shadcn/ui (42 components, 29 backed by Radix), and has MUI 7.3.5 installed but completely unused (zero imports across the entire codebase). A design system survey revealed five problems:

  1. MUI + Emotion are dead weight — 4 packages, ~1.5MB, zero imports. No ThemeProvider, no createTheme, no component usage.
  2. next-themes is installed but non-functionaluseTheme() is called in sonner.tsx, but no ThemeProvider wraps the application. The .dark class on <html> (which Tailwind v4 uses) is never toggled by JS.
  3. fonts.css is an empty placeholder — imported by index.css but contains zero @font-face declarations.
  4. Two-tier color strategy — shadcn/ui primitives use semantic tokens (bg-primary, text-muted-foreground), but business components use raw Tailwind palette colors (bg-red-50, text-green-700).
  5. Token gaps in dark mode--input-background, --switch-background, --font-size, --font-weight-* have no dark mode values.

Decision

1. Component library: shadcn/ui (keep), MUI (remove)

Keep shadcn/ui as the sole component library. It's already integrated, backed by Radix primitives, and uses Tailwind tokens natively.

Remove @mui/material, @mui/icons-material, @emotion/react, and @emotion/styled from dependencies. They have zero imports and represent ~1.5MB of dead bundle weight.

Rationale: Adding MUI alongside shadcn/ui would create a dual design system with conflicting styling approaches (CSS-in-JS vs utility classes), inconsistent accessibility APIs, and double the bundle size. MUI was never adopted; removing it eliminates dead code and simplifies the dependency tree.

2. Theme switching: our own useTheme() hook (superseded by ADR-0019, q.v.)

Superseded. ADR-0019 replaces next-themes with a custom themes.ts hook using data-theme attribute, supporting 4 built-in themes (light, warm, dark, ijcp) and user custom themes post-MVP.

3. Typography: self-hosted variable fonts

Use Inter (sans-serif, weight 100–900) and JetBrains Mono (monospace, weight 400–700) as self-hosted WOFF2 variable fonts via @font-face in fonts.css.

Rationale: Self-hosting avoids Google Fonts' GDPR/LGPD consent requirements and eliminates a render-blocking external request. Variable fonts provide the full weight range in a single file (~300KB for Inter, ~150KB for JetBrains Mono). Both are open-source (SIL OFL).

4. Design token format: CSS custom properties (primary), JSON export (secondary)

Keep CSS custom properties in theme.css as the source of truth. Generate a JSON export for external tooling (Figma, documentation). Do not create a TypeScript theme object — Tailwind v4's @theme inline bridge is the single mapping layer.

Rationale: The CSS is already the source of truth (181 lines, 39 tokens). A TypeScript theme object would be a duplicate that drifts. If tooling needs tokens, generate JSON from the CSS.

5. Color strategy: semantic tokens everywhere

Require all components (business and UI) to use semantic design tokens (bg-primary, text-muted-foreground). Audit and replace raw Tailwind palette classes (bg-red-50, text-green-700) with semantic equivalents.

Rationale: The current split creates a situation where changing a color in theme.css has no effect on business components. Semantic tokens are the design system's contract — using them consistently ensures theme changes propagate everywhere.

Consequences

Positive

  • Removes ~1.5MB of dead dependencies
  • Activates dark mode toggle (already defined in CSS, never wired up)
  • Single source of truth for colors in theme.css
  • Self-hosted fonts eliminate GDPR consent requirement
  • Bundle size reduction measurable in CI

Negative

  • Breaking change: removes MUI from dependency tree (mitigation: zero imports, no runtime impact)
  • Font files add ~450KB to the repository (mitigation: WOFF2, variable fonts, single request)
  • Color audit is manual work across ~15 component files

Neutral

  • next-themes stays (already a dependency)
  • shadcn/ui is unchanged
  • Tailwind v4 configuration is unchanged

References

Distribuído sob licença MIT.