Dark mode via semantic-token migration across 74 frontend files
nwhitehouse added light/dark/system theme support. The bulk of the diff isn't adding `dark:` Tailwind variants; it's migrating raw color classes to the semantic tokens that were already defined in `globals.css`, which made both modes work at once.
The theme implementation is three parts. A ThemeContext holds the system | light | dark value. A pre-hydration <script> in layout.tsx reads localStorage('olava.theme'), resolves the system preference, and sets className=dark plus style.colorScheme before React mounts - the standard SSR technique for avoiding a light flash. A ThemeToggle segmented control surfaces on the /account Appearance section.
The 74-file diff is the token migration sweep. The codebase had semantic tokens defined in globals.css (bg-card, text-foreground, border-border, bg-muted, text-muted-foreground, bg-secondary, and so on) with both light and dark token sets already present. The components just hadn't been updated to use them - they were still hardcoded to bg-white, text-gray-700, border-gray-200, and the like. Swapping those out is what delivered dark mode across roughly 80 components in one pass, without adding dark: variants per-element.
A follow-up polish commit landed within hours: five components that render the branded SVG mark (InitialView, AppSidebar, WorkflowList, site-logo, onit-status-icon) needed a block dark:hidden / hidden dark:block dual-image pattern to swap between color and white variants. The commit also fixes the chat send button, which had a black gradient that was invisible in dark mode - it now switches to a white gradient via dark:from-neutral-100 dark:to-white.
The polish commit is predictable: a sweep this size will miss spots. nwhitehouse caught two within hours of the main commit. Expect a few more if you port this.
Spotted something wrong? Or know the PR text has fresher detail than the writeup above?