- CSS 54.7%
- HTML 22.9%
- JavaScript 20.3%
- Dockerfile 2.1%
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 |
||
|---|---|---|
| .github/workflows | ||
| .dockerignore | ||
| .gitignore | ||
| app.js | ||
| compose.yaml | ||
| Dockerfile | ||
| index.html | ||
| LICENSE | ||
| README.md | ||
| styles.css | ||
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 withmatchMedia("(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-pressedtells 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.