You might be surprised how little there is to know. There are basically three strategies: 1. set your light theme color variables in :root and your dark theme colors in `@media (prefers-color-scheme: dark) { :root { ... } }` 2. use the newish `--var: light-dark(light-value, dark-value)` syntax with `:root { color-scheme: light dark; }` 3. use a class on the body to determine whether to apply light or dark variables (can be used in combination with the above to default to user’s current theme while letting javascript toggle the theme by toggling the class)
If you're just concerned about light vs dark, then this article (which was posted to HN) does an auto/light/dark toggle button without javascript, and it shows using CSS variables for your colors: https://lyra.horse/blog/2025/08/you-dont-need-js/
At first, I created a simple API via Python that was basically mimicking the localStorage API. It would accept getItem and setItem requests and write it to a file. In the HTML file I was replacing localstorage calls to calls to the API automatically, via a script that was doing a simple search-and-replace on the file. I was also assigning a custom subdomains via nginx to each html file, so that it could have the max 10MB storage that localstorage can have.
After a while it became clunky doing things with separate scripts, so I ended up creating - htmlsync.io. It's still pre-alpha, but registrations for the free tier are open.