tailwind1-8 Exercise 2: A Light / Dark / System Toggle ======================================================= Step 1: make dark: depend on a class. In your CSS: @import "tailwindcss"; @custom-variant dark (&:where(.dark, .dark *)); Step 2: the page. The theme script is inline in the head, so it runs before the body is parsed.
theme demo
How the three choices are stored: Light localStorage.theme = 'light' Dark localStorage.theme = 'dark' System the key is removed (no stored choice means "follow the system") Prove it. In DevTools, change the system setting under Rendering > "Emulate CSS media feature prefers-color-scheme", click the buttons, and check document.documentElement.classList.contains('dark') and localStorage.theme after each step. Measured (emulated system setting; real clicks): 1. fresh visit, system light dark class: false stored: nothing 2. system switches to dark with the page open dark class: true (the change listener ran) 3. click Light (system still dark) dark class: false stored: light 4. system light, click Dark dark class: true stored: dark 5. reload dark class: true and it was already true when the body started parsing; stored: dark; box background gray-900 6. click System (system is light) dark class: false stored: removed 7. system switches to dark dark class: true (following the system again) The head-versus-end comparison, with the same code at the end of the body instead of in the head (system dark, nothing stored): script in the head: class dark at the start of the body: true script at the end of body: class dark at the start of the body: false (it was added only afterwards) WHY THIS WORKS AS AN ANSWER --------------------------- The class-based variant means the page is dark exactly when has the class dark, so the script's only job is to decide whether to set it. The decision is: an explicit stored 'dark' wins; an explicit 'light' wins; with nothing stored, ask the system. Step 3 shows an explicit choice ignoring the system (light page, dark system), step 4 the reverse, and steps 2 and 7 show system mode following changes live through the change listener. Step 5 is why the script must be inline in the head: the stored choice is applied before the body exists, so the first paint can already be dark. With the script at the end the class is added only after the page has been parsed (false at the start of the body), and the browser may paint the light version first. I measured when the class is set, not the paint, so this shows the mechanism rather than a screenshot of the flash. In a real site keep the head script tiny and synchronous (no defer or async), and give the toggle buttons accessible labels.