Skip to content

fix(web): apply Storybook dark theme to portaled UI - #57

Merged
zooidbot merged 1 commit into
mainfrom
fix/42-storybook-dark-portal
Oct 7, 2026
Merged

zooidbot merged 1 commit into
mainfrom
fix/42-storybook-dark-portal

Conversation

@zooidbot

@zooidbot zooidbot commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator

Closes #42

Repro: in Storybook, select Dark in the Theme toolbar and open a dialog story (e.g. dialogs-sharemessagedialog--no-room). The dialog renders light.

Cause: the decorator wrapped stories in <div className="dark">. The dark variant is &:is(.dark *), and Radix portals render into document.body, outside that div.

Change: packages/web/.storybook/preview.tsx now toggles .dark on document.documentElement (as ThemeProvider does) from a ThemeRoot effect, and removes it on unmount.

Knowledge impact: none (ZNC003's ThemeProvider contract is unchanged; this is a dev-harness fix)

Tests: pnpm typecheck and pnpm test in packages/web pass (154 files, 981 tests). The repo has no lint script. UX Tester (round 2; round 1 failed to start on a path error in my brief): ✅ no-room Dark, ✅ dropdown-open Dark (dialog and dropdown both dark), ⚠️ no-room Light renders light.

Not verified: that <html> has no leftover .dark after switching back to Light (UX Tester didn't check the class list; it relies on the effect cleanup/toggle). A non-portaled story (UI/Badge) in both themes was not checked, since UX Tester couldn't locate the story id. Both are worth a quick look in Storybook.

Plan feedback: The issue named the right file and fix. My first UX Tester brief said packages/web for Storybook and it was appended to the worktree path twice, which cost a round. The issue also named no concrete non-portaled story id (UI/Badge wasn't found by id), so a specific one would help.

🤖 Generated with Claude Code

Toggle .dark on document.documentElement (as ThemeProvider does) instead of a
wrapper div, so dialogs, menus and popovers rendered into document.body follow
the theme toolbar. Remove it on unmount so Light leaves no .dark on <html>.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@zooidbot zooidbot left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed against clients#42. No blocking findings.

  • The fix matches the issue. .dark now lives on <html>, so Radix portals in document.body pick up the variant. This is the same place theme-provider.tsx puts it.
  • Leftover .dark after switching to Light: the code covers this, not just the cleanup. When dark changes, the cleanup removes the class, and then the effect runs classList.toggle("dark", false), which removes it explicitly. Nothing can leave it set.
  • Non-portaled stories: the inner bg-background text-foreground p-6 wrapper is unchanged, and &:is(.dark *) matches every descendant of <html> just as it matched every descendant of the old wrapper div. Rendering is the same in both themes.

So I'm treating both Not verified items as settled by reading the code. Merging once CI is green.

@zooidbot
zooidbot merged commit b041ea3 into main Oct 7, 2026
1 check passed
@zooidbot
zooidbot deleted the fix/42-storybook-dark-portal branch October 7, 2026 21:09
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.

Storybook dark theme doesn't apply to portaled UI (dialogs, menus, popovers)

1 participant