Skip to content

fix(desktop): bind Tailwind dark variant to the theme class#2849

Open
oscarjpicazo wants to merge 1 commit into
block:mainfrom
oscarjpicazo:fix/tailwind-dark-variant-theme-class
Open

fix(desktop): bind Tailwind dark variant to the theme class#2849
oscarjpicazo wants to merge 1 commit into
block:mainfrom
oscarjpicazo:fix/tailwind-dark-variant-theme-class

Conversation

@oscarjpicazo

Copy link
Copy Markdown

Fixes #2848

What problem this solves

The app themes itself by class — ThemeProvider sets .dark/.light on
<html>, seeded before first paint by the synchronous bootstrap in
desktop/index.html. But on Tailwind v4 the dark: variant defaults to the
prefers-color-scheme media query, and nothing in the repo rebinds it: the
@config-loaded tailwind.config.js has no darkMode key and there is no
@custom-variant declaration.

So the 127 dark: utilities in desktop/src (and 166 in web/src) follow
the OS appearance while every CSS-variable style follows the selected
theme
. Pinning a light theme with the OS in dark mode (or the reverse)
renders mixed styling — e.g. the composer's dark glass treatment
(dark:bg-background/70 dark:backdrop-blur-xl) over a light theme.

How it was implemented

One @custom-variant line (plus a comment) in each Tailwind entry point:

  • desktop/src/shared/styles/globals.css
  • web/src/shared/styles/globals.css
@custom-variant dark (&:where(.dark, .dark *));

This is Tailwind v4's canonical way to switch dark: to class-driven dark
mode. :where() keeps specificity at zero, so no existing selector wins or
loses against other rules — the only change is when dark: applies.

Both entry points are fixed in one PR because it is one logical defect (the
missing variant binding) in the two places the same pattern exists. The web
client's ThemeProvider sets the same class; today it always resolves
"system" in production, so this is a latent correctness fix there rather than
a visible behavior change.

How to test it manually

  1. OS in dark appearance → Settings → Appearance → Light mode, any light
    theme. Before: composer/header/settings surfaces show dark-mode treatments.
    After: fully light rendering.
  2. OS in light appearance → explicit dark theme. Before: dark: styling never
    applies. After: dark treatments apply.
  3. "Follow system" mode: unchanged in both OS appearances (class and media
    query agree by construction).

Compiled-CSS verification: building globals.css with the repo config
(Tailwind CLI 4.3.x) previously emitted every dark: utility inside
@media (prefers-color-scheme: dark); after this change zero
prefers-color-scheme occurrences remain and utilities compile to
.dark\:…:where(.dark, .dark *).

Why no automated test

The defect lives in the Tailwind build's variant expansion, not in runtime
code — asserting it would mean compiling the stylesheet and string-matching
generated CSS, which pins Tailwind's output format rather than app behavior.
The compiled-CSS before/after above documents the verification performed.

Regression audit performed

Every dark: usage was audited against the new binding:

  • Desktop has a single HTML entry and a single Tauri window (no
    WebviewWindowBuilder anywhere), so the index.html bootstrap covers all
    first paints. Web's ThemeProvider applies the class synchronously inside
    its useState initializer and is the outermost rendered node, so nothing
    paints before the class exists.
  • The OS-driven onboarding surfaces (data-system-color-scheme +
    .buzz-onboarding-neutral-theme) contain no dark: utilities, so they are
    unaffected.
  • The two portals that force a literal dark class
    (shared/ui/markdown.tsx lightbox, shared/ui/VideoPlayer.tsx review
    overlay) now get dark: utilities active inside them — which is what their
    own stylesheet documents as intended (globals/video-review.css: "the
    shadcn neutral dark palette regardless of the app theme"). Before this fix
    those overlays were internally inconsistent (dark variables, OS-driven
    utilities).
  • No CSS uses @media (prefers-color-scheme) and no @apply dark: exists,
    so there is nothing left half-bound.
  • On web today the change is a functional no-op in production (theme is
    pinned to "system", so class ⟺ media query), and it makes the dev-only
    ?previewTheme= override actually preview correctly.

Follow-up deferred

  • web/index.html could seed the theme class before first paint like
    desktop/index.html does — defense in depth for whenever web exposes its
    (currently unmounted) ThemeToggle.
  • A comment in globals/components.css noting that
    .buzz-onboarding-neutral-theme subtrees should use semantic variables,
    not dark: utilities, since those surfaces intentionally follow the OS.

@oscarjpicazo
oscarjpicazo requested a review from a team as a code owner July 25, 2026 14:56
Tailwind v4's dark: variant defaults to the prefers-color-scheme media
query, but the app's ThemeProvider drives theming by setting .dark/.light
on <html>. Without a @custom-variant binding, all dark: utilities followed
the OS appearance instead of the selected theme: picking an explicit light
theme while the OS is in dark mode (or vice versa) rendered mixed styling,
e.g. the composer's dark glass treatment over a light theme.

Bind dark: to the theme class in both Tailwind entry points (desktop and
web). The desktop's synchronous theme bootstrap in index.html already
guarantees the class exists before first paint.

Signed-off-by: oscarjpicazo <n.oscarjuanpicazo@gmail.com>
@oscarjpicazo
oscarjpicazo force-pushed the fix/tailwind-dark-variant-theme-class branch from 5b9a0a9 to 619f3fa Compare July 25, 2026 14:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] dark: utilities follow the OS color scheme, not the selected theme (missing Tailwind v4 @custom-variant binding)

1 participant