Skip to content

Dark Mode

  1. Include ThemeScript before your content to prevent FOUC:
@layout.Base(layout.DefaultPageProps()) {
@layout.ThemeScript("")
{@content}
}
  1. Add the @custom-variant directive in your CSS:
@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
  1. Optionally add a ThemeToggle:
@layout.ThemeToggle("Toggle theme", "")
  • ThemeScript runs before first paint, reads localStorage, and adds the .dark class to <html>.
  • color-scheme: light on :root, color-scheme: dark on .dark — native form controls render correctly in both modes.
  • ThemeToggle syncs across all instances on the page via querySelectorAll.
Context Light Dark
Semantic background bg-blue-600 dark:bg-blue-500
Semantic text text-blue-600 dark:text-blue-400
Neutral text text-gray-500 dark:text-gray-400

Every neutral and semantic color class has a dark: variant — enforced by regression tests (TestDarkModeCompliance, TestDarkModeSemanticColors).

ThemeScript and ThemeToggle both wrap localStorage access in try/catch to handle Safari private mode (QuotaExceededError).