The browser has had prefers-color-scheme for years, it reads the operating system preference and exposes it as a CSS media query, and using it as the default is a good starting point because the user told their OS what they want and that preference deserves to be honored. It is not, however, the complete answer to what the user actually needs on any given site.
The OS Setting Is a Default, Not a Contract
A user might run their OS in dark mode because they work in low-light conditions at night, and still prefer a specific application in light mode because the content is easier to read that way - a document editor, a data table, something with dense information where contrast works differently. The OS preference is a reasonable starting point, not a mandate.
Respecting prefers-color-scheme gets you ninety percent of the way there. The remaining ten percent is giving the user a way to say: for this site, I want the other one. And remembering that choice the next time they come back.
This is a two-layer model: system preference as the default, per-site user override stored locally. Both layers matter. A site that only reads the OS preference removes the user's ability to make a site-specific decision. A site that ignores the OS preference entirely forces the user to make a choice before they've seen anything.
The Implementation Is Not Complex
The pattern is straightforward: start with the OS preference via the media query, add a toggle the user can reach without effort - one click, visible but not intrusive - and when they toggle, store the choice in a cookie that travels with every subsequent request, which means the server can read it before the first byte of HTML is sent and set the data-theme attribute on the document root before the page renders. The rest is CSS custom properties: every color in the application references a variable, and the variable changes when the theme changes. No flash of the wrong theme, no client-side correction after the fact, no framework required, because the whole mechanism is a cookie, a server that reads it before rendering, and a CSS custom property system that responds to the attribute.
The system preference is a suggestion. The stored preference is a decision. Treat them differently.
Why Both Layers Exist
There is another reason the per-site override matters that rarely gets said directly. The author uses dark mode, so the dark theme is polished, tested, and visually coherent. The light theme exists because it was expected to, but it was built in an afternoon and hasn't been touched since. Or the reverse.
Most applications only have one theme that was actually finished.
The user who opens the app and finds the alternative theme barely functional is not wrong to want to switch - they are responding to reality. The per-site toggle gives them a way to use the application optimally without changing their OS setting to do it.
A developer who implements only the media query is saying: I trust the OS setting more than I trust the user to know what they want on this specific site. A developer who implements only a manual toggle is saying: I don't care what the user told their OS. Neither is correct on its own.
The combination says: I read your system preference, I used it as your starting point, and I gave you a way to change it here without changing it everywhere. That is the respectful behavior, and it takes about thirty lines of code to implement correctly.
Reepolee generates this pattern by default. The theme toggle is part of the layout, the preference is stored in a cookie and read server-side on every request, and the color system is built on CSS custom properties from the start - which means the application ships with both layers already working, no flash of wrong theme, and the developer's only job is to define the color values for each theme.