Skip to content

feat: add config-driven DaisyUI theming system with ThemeToggle component#30

Open
mzngo wants to merge 1 commit into
masterfrom
feat/theming-system
Open

feat: add config-driven DaisyUI theming system with ThemeToggle component#30
mzngo wants to merge 1 commit into
masterfrom
feat/theming-system

Conversation

@mzngo
Copy link
Copy Markdown
Collaborator

@mzngo mzngo commented Apr 9, 2026

Summary

Implements Phase 1.3 of the roadmap: a config-driven light/dark theming system using DaisyUI v5's built-in theme support.

Problem

The starter had no theming system — it used DaisyUI's default theme with no way to customize it or let users toggle between light and dark mode. Anyone forking the starter would need to figure out DaisyUI theming from scratch, and the site would show a flash of the wrong theme on first load for users whose system preference differed from the default.

Changes

branding.config.ts

  • Added ThemeConfig interface with light and dark fields (any DaisyUI v5 theme name)
  • Added optional theme field to BrandingConfig — omitting it gracefully falls back to 'light' / 'dark'
  • Default config uses corporate (light) and business (dark) as polished, professional starting themes

src/components/ThemeToggle.astro (new)

  • DaisyUI swap swap-rotate button with Sun/Moon Lucide icons
  • Reads the active theme names from data-theme-light / data-theme-dark attributes on <html>, keeping the component decoupled from branding.config.ts at runtime
  • Persists user preference to localStorage; syncs with prefers-color-scheme when no saved preference exists

src/layouts/Layout.astro

  • Embeds theme names from brandingConfig.theme as data-theme-light / data-theme-dark attributes on <html>
  • Adds a tiny inline is:inline script that runs before first paint to set data-theme correctly, preventing any flash of the wrong theme (FODT)

src/components/Header.astro

  • Mounts <ThemeToggle /> in the navbar-end slot, next to the CTA button

src/styles/global.css

  • Updates @plugin "daisyui" to explicitly list only the two configured themes (corporate --default, business --prefersdark)
  • Keeps the CSS bundle lean — DaisyUI only compiles theme CSS for themes listed here
  • Adds a comment pointing maintainers to update this in sync with branding.config.ts

Benefits

  • Zero new dependencies — uses DaisyUI's built-in theming and localStorage
  • No flash of wrong theme — inline script resolves theme before browser paints
  • Config-driven — changing the two theme names in branding.config.ts is all a customizer needs to do
  • Lean CSS output — only the declared themes are compiled into the bundle
  • Accessible — toggle button has an aria-label; Sun/Moon icons clearly communicate state

Validation

  • npm run lint — ✅ no errors
  • npm run check — ✅ 0 errors, 0 warnings
  • npm run build — ✅ builds cleanly, all pages generated

References

…nent

- Add ThemeConfig interface and theme.light / theme.dark fields to BrandingConfig
  in branding.config.ts. Defaults to 'corporate' (light) and 'business' (dark).
- Add ThemeToggle component (Sun/Moon swap button, DaisyUI swap-rotate) that persists
  the user's preference to localStorage and syncs with system prefers-color-scheme.
- Add an anti-FODT inline script in Layout.astro that runs before first paint, reading
  the configured theme names from data-theme-light / data-theme-dark attributes on
  <html> so the correct theme is applied before any content renders.
- Update global.css @plugin config to declare the two configured themes explicitly,
  keeping the CSS bundle lean (only included themes are compiled).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant