1
Fork 0
A light/dark theme switch done the proper way — CSS variables, one attribute on <html>, and a remembered choice. It starts from the visitor's system setting and never flashes the wrong theme on load. https://devai.io/templates/dark-mode-toggle
  • CSS 54.7%
  • HTML 22.9%
  • JavaScript 20.3%
  • Dockerfile 2.1%
Find a file Use this template
Leonardo Devai b0c420a672 Review and modernize all 42 projects to the updated standard
Pinned 2026 toolchains (Go 1.26, Rust 1.98/edition 2024, Python 3.14 + uv, Node 24,
Zig 0.16, NixOS 26.05), postgres 18 / mongo 8, lockfiles built from, non-root
runtimes, .dockerignore, per-project LICENSE, READMEs with the git.devai.io clone
line, checkout@v7 CI. Security fixes in the legacy Rust APIs (any-password login,
self-assigned admin, hard-coded JWT secret), JWT alg/exp/sub enforcement across the
blog series, safe markdown links in the frontends, and many smaller bugs — every
project was built, run and exercised end to end.

Adds scripts/publish.sh + a CI publish job that splits every folder into its own
repo at git.devai.io/templates/<folder>.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01128fhuZbgivaSJvtMf4s1G
2026-09-27 21:10:38 +02:00
.github/workflows Review and modernize all 42 projects to the updated standard 2026-09-27 21:10:38 +02:00
.dockerignore Review and modernize all 42 projects to the updated standard 2026-09-27 21:10:38 +02:00
.gitignore Standardize projects: compose.yaml, ./data state, CI workflow, GUIDELINES 2026-09-27 20:07:30 +02:00
app.js Review and modernize all 42 projects to the updated standard 2026-09-27 21:10:38 +02:00
compose.yaml Review and modernize all 42 projects to the updated standard 2026-09-27 21:10:38 +02:00
Dockerfile Review and modernize all 42 projects to the updated standard 2026-09-27 21:10:38 +02:00
index.html Review and modernize all 42 projects to the updated standard 2026-09-27 21:10:38 +02:00
LICENSE Review and modernize all 42 projects to the updated standard 2026-09-27 21:10:38 +02:00
README.md Review and modernize all 42 projects to the updated standard 2026-09-27 21:10:38 +02:00
styles.css Review and modernize all 42 projects to the updated standard 2026-09-27 21:10:38 +02:00

dark-mode-toggle

A light/dark theme switch done the proper way — CSS variables, one attribute on <html>, and a remembered choice. It starts from the visitor's system setting and never flashes the wrong theme on load.

Run

Get it: git clone https://git.devai.io/templates/dark-mode-toggle.git

Double-click index.html — it opens in your browser and works. Nothing to install, no build step.

Or serve it like production:

docker compose up --build

Then open http://localhost:8080.

How it works

Every color on the page is a CSS variable like var(--bg). styles.css defines them twice:

[data-theme="dark"]  { --bg: #0b0c0e; --text: #e7e9ee; }
[data-theme="light"] { --bg: #f5f7f9; --text: #14161a; }

To switch themes, JavaScript changes one thing:

document.documentElement.dataset.theme = "light";

and every var(--bg) on the page picks up the new value. No element is touched individually.

  • First visit: there's no saved choice, so initialTheme() asks the OS with matchMedia("(prefers-color-scheme: light)"). Before any JavaScript runs, the CSS media query styles :root:not([data-theme]) the same way.
  • Clicking the button flips the theme and saves it to localStorage. Only a real click is saved, so until you click, every visit follows the OS setting.
  • No flash: a tiny <script> in <head> applies the saved theme before the page is first drawn.
  • aria-pressed tells screen readers whether dark mode is on; the button's label stays "Dark mode" so the state is never ambiguous.

Try it: change --accent to any color, add a third "System" option that removes data-theme and the saved key, or tweak the transition speeds.

Layout

index.html   the toggle button, a preview card, and the no-flash <script>
app.js       initialTheme(), applyTheme() and the click handler
styles.css   the two palettes as CSS variables

Deploy

Push to your own GitHub repo and the shipped workflow (.github/workflows/ci.yml) tests the compose stack, publishes the image to GHCR, and — once you set the DEPLOY_HOST / DEPLOY_USER variables and DEPLOY_KEY secret — deploys it to your server over ssh.


Part of devai.io — the Web Basics track: HTML, CSS & JavaScript, one concept at a time. Next up: image-slideshow.