Skip to main content

Source: ocean/docs/ui-ux/theming-ui-ux-assessment-2025-08-29.md | ✏️ Edit on GitHub

Ocean UI/UX Theming Assessment — 2025-08-29

Generated at 2025-08-29T11:33:03-04:00

Author: Design Systems & DX Audit


Executive Summary

  • Architecture fit: The stack (Tailwind 4 + @theme, CSS variables, next-themes, class-variance-authority, shadcn/ui components) is well aligned for scalable theming and long-term UI/UX evolution.
  • Strengths: Clear separation between semantic tokens and component usage; palette-driven themes; light/dark handled at the root; shadcn components consume semantic utilities.
  • Gaps: Potential FOUC on palette switch, a stale tokens.generated.css artifact with wrong units, missing automated contrast checks, and lack of guardrails to prevent raw color usage.
  • Verdict: Ready to grow and customize shadcn/ui. Address the minor gaps to ensure robustness, a11y, and contributor ergonomics.

System Overview

  • Color palettes: design-tokens/palettes.json defines semantic colors (background, foreground, primary, etc.) with light and dark variants per palette (neutral, stone, zinc, gray, slate).
  • Runtime variables: src/styles/color-themes.generated.css sets CSS variables for each palette via :root[data-color-theme] and .dark[…] overrides.
  • Tailwind mapping: src/styles.css maps semantic CSS variables to Tailwind color tokens via @theme (e.g., --color-primary: var(--primary)), enabling utilities like bg-primary, text-muted-foreground.
  • Dark mode: next-themes in src/routes/__root.tsx controls class="dark" on the root.
  • Palette selection: useColorTheme() in src/hooks/use-color-theme.ts writes data-color-theme to document.documentElement and persists it in localStorage.
  • Components: shadcn/ui (e.g., src/components/ui/button.tsx) use cva variants and semantic Tailwind utilities.

Diagram — Token and Theme Flow


Runtime Theming Integration

  • Dark mode: ThemeProvider sets .dark class based on user/system preference.
  • Palette: useColorTheme() sets data-color-theme to match selected palette.
  • Result: CSS variables from color-themes.generated.css resolve for both light and dark.

Key files:

  • src/routes/__root.tsx — wraps app with ThemeProvider.
  • src/hooks/use-color-theme.ts — sets data-color-theme + localStorage.
  • src/styles/color-themes.generated.css — variables for each palette/mode.

Component Theming (shadcn/ui)

  • Pattern: Use semantic Tailwind utilities that map to CSS vars (no raw colors).
  • Example: src/components/ui/button.tsx uses cva variants and classes like bg-primary, text-primary-foreground, border-border.

Guidelines:

  • Do: add/extend cva variants; keep using semantic classes.
  • Avoid: hex/rgb/hsl/oklch literals in TSX/CSS; prefer variables/utilities.

Accessibility (Contrast)

  • Pairs to verify (AA minimum, AAA preferred for text):
    • foreground on background
    • primary-foreground on primary
    • secondary-foreground on secondary
    • muted-foreground on muted
    • accent-foreground on accent
    • destructive-foreground on destructive

Developer Ergonomics & DX

  • Pros:
    • Single semantic surface for colors via Tailwind utilities.
    • cva variants centralize component options.
    • Palettes can evolve without component churn.
  • Improvements:
    • Pre-hydration script to avoid FOUC.
    • Guardrails (ESLint/CI) to block raw colors.
    • Docs for shadcn customization and update policy.

Risks & Gaps

  • FOUC risk: data-color-theme set in useEffect (post-paint).
  • Stale artifact: src/styles/tokens.generated.css appears unused and uses incorrect px units (fractions like 0.625px).
  • No automated contrast checks.
  • No lint rule preventing raw color usage in components.
  • Minor duplication: sidebar token remapping can be consolidated.

Recommendations (Actionable)

  1. No-flash theming: Inline pre-hydration script in index.html to set data-color-theme early; next-themes already handles .dark preflight.
  2. Token cleanup: Remove or fix src/styles/tokens.generated.css; keep src/styles/tokens.css canonical (use rem units).
  3. Contrast CI: Add tools/check-contrast.ts to validate semantic pairs per palette/mode; gate PRs.
  4. Guardrails: ESLint rule/CI grep to forbid color literals in src/**/*.{ts,tsx,css} (allow in token/generator paths).
  5. Docs: Short contributor doc on customizing shadcn components with tokens, and when to fork vs. extend.
  6. Optional: Generate TS types/metadata from design-tokens/palettes.json to keep src/config/themes.ts in sync or replace it.

Implementation Plan (Phased)

  • Phase 1 (Same-day)

    • Add pre-hydration no-flash script.
    • Remove or correct src/styles/tokens.generated.css.
    • Write shadcn customization guide.
  • Phase 2 (This week)

    • Add contrast checker script + CI job.
    • Add ESLint/CI rule to block raw colors.
    • Consolidate sidebar tokens if needed.
  • Phase 3 (Later)

    • Generate TS types from palettes; reduce duplication.
    • Add visual regression tests per palette/mode.

Appendix — Code References

  • src/styles.css — Tailwind @theme mapping and derived status tokens.
  • src/styles/color-themes.generated.css — per-palette CSS vars (light/dark).
  • src/styles/tokens.css — base tokens (radius, spacing, typography, etc.).
  • src/hooks/use-color-theme.ts — palette selection and persistence.
  • src/routes/__root.tsxThemeProvider wrapper for dark mode.
  • src/components/ui/button.tsx — shadcn/cva usage with semantic utilities.

Mermaid — Update Workflow (Design → Code)

Mermaid — Theme Switching Timing