コンテンツにスキップ
まだあたたかい

Theming, dark mode, and customising the look

How the chirpy-light / chirpy-dark daisyUI themes are wired, where to change colours, the no-FOUC theme toggle, and View Transitions for animated swaps.

Tutorials 3 分で読めます

The theme ships with two custom daisyUI v5 themes — chirpy-light (default) and chirpy-dark — both authored from scratch in OKLCH so the palette stays perceptually balanced. Switching themes uses the View Transitions API where supported, giving you a circular reveal from the toggle button.

Open the toggle in the sidebar (the sun/moon icon) to try it now. Then read on for how to customise it.

src/styles/global.css is the single entry point. It imports Tailwind v4 with one line and registers daisyUI plus the two custom themes:

src/styles/global.css
@import 'tailwindcss';
@plugin 'daisyui' {
themes: false;
logs: false;
}
@plugin 'daisyui/theme' {
name: 'chirpy-light';
default: true;
--color-base-100: oklch(100% 0 0);
--color-primary: oklch(45% 0.135 264);
/* ... */
}
@plugin 'daisyui/theme' {
name: 'chirpy-dark';
prefersdark: true;
--color-base-100: oklch(22% 0.005 264);
--color-primary: oklch(72% 0.11 264);
/* ... */
}

The Vite plugin @tailwindcss/vite is registered in astro.config.mjs.

Open src/styles/global.css and edit the OKLCH values inside each @plugin "daisyui/theme" block. The token names (--color-primary, --color-base-100, …) are the canonical daisyUI v5 variables — you can also add your own custom tokens, like the theme’s existing --color-sidebar-from, --color-sidebar-to, --width-sidebar, etc.

Why OKLCH? It’s perceptually uniform, so a 10% lightness change feels like a 10% lightness change, not “much darker for orange but barely visible for blue”. Tools like oklch.com make authoring painless.

src/components/islands/ThemeToggle.astro is small but does several things at once:

  • Stores the choice in localStorage under the key theme.
  • Falls back to prefers-color-scheme: dark when no choice is pinned.
  • Emits a theme-change CustomEvent so other islands (Giscus, for example) can react.
  • Animates the swap with the View Transitions API: a circular reveal centred on the cursor that respects prefers-reduced-motion: reduce.

The toggle persists the user’s preference, but localStorage reads happen in JavaScript — and JavaScript runs after CSS paints. To prevent a flash of the wrong theme on first load, BaseLayout.astro ships a tiny <script is:inline> block that:

  1. Reads localStorage.theme.
  2. Falls back to matchMedia('(prefers-color-scheme: dark)').
  3. Sets document.documentElement.dataset.theme before any styles paint.

Result: the page renders in the correct theme from frame zero, even on slow connections.

Expressive Code is configured with two themes (github-light and github-dark-dimmed) and bound to the site’s data-theme attribute via themeCssSelector. When the user toggles theme, every code block on the page instantly switches palette without a JavaScript re-render. See the code blocks post for details.

Beyond daisyUI’s standard palette, the theme defines a few layout tokens — change them in global.css to retune sizing:

TokenDefaultUsed for
--width-sidebar18remLeft sidebar width
--width-panel14remRight “Trending tags” panel
--height-topbar3.25remTop bar height
--width-prose50remReading column max width
--color-sidebar-fromOKLCHSidebar gradient start
--color-sidebar-toOKLCHSidebar gradient end
--color-sidebar-textOKLCHSidebar text colour

If you’d rather call the themes dawn and dusk, rename them in two places:

  1. @plugin "daisyui/theme" { name: "dawn"; ... } in global.css.
  2. The strings "chirpy-light" and "chirpy-dark" in ThemeToggle.astro, BaseLayout.astro (no-FOUC script), and Giscus.astro (theme sync).

Search for chirpy- across the workspace — you’ll find every occurrence in well under a dozen places.

daisyUI happily registers as many themes as you like. Add another @plugin "daisyui/theme" block and extend the toggle to cycle through three states (or build a separate dropdown). The theme-change event still fires and Giscus / Expressive Code adapt without further work.

If you want a hard cut instead of the circular reveal:

src/components/islands/ThemeToggle.astro
// Remove the document.startViewTransition wrapper:
if (document.startViewTransition) {
document.startViewTransition(applyTheme);
} else {
applyTheme();
}
applyTheme();

Or leave the code intact — users with prefers-reduced-motion: reduce already get a hard cut.