feat: update dark mode mixin - #1596
Merged
Merged
Conversation
chrisschnaars
marked this pull request as draft
August 17, 2026 21:10
Contributor
|
chrisschnaars
marked this pull request as ready for review
August 18, 2026 21:37
runnabro
approved these changes
Aug 18, 2026
Collaborator
This PR was released!🚀 Changes included in v15.1.0 |
5 tasks
chrisschnaars
added a commit
that referenced
this pull request
Sep 3, 2026
…dCompiler (#1608) ## Summary - `tailwindCompiler`/`TailwindStyle` accept a new `darkModeRootSelector` option. When supplied, the generated `dark:` custom-variant is scoped to that root's own `[darkModeDataAttribute]`, self or descendant, instead of matching the attribute on *any* ancestor. - Omitted (the default), behavior is byte-for-byte unchanged from today. - Mirrors `when-color-mode-dark($root)` (`styles/mixins/when-color-mode-dark.scss`, added in #1596) — same rationale: a page can have two independent `data-color-mode` scopes (e.g. readme's SuperHub admin shell vs. the hub content it's previewing), and the current ancestor-matching selector can't tell them apart. No default root selector is provided, for the same reason `when-color-mode-dark` doesn't have one — see that mixin's doc comment, and the new doc comment on `tailwindCompiler`'s `darkModeRootSelector` param, for the full reasoning. ## Test plan - [x] `__tests__/utils/tailwind-compiler.test.ts` (new) — unit-tests the generated `@custom-variant dark (...)` string directly (spies on `tailwindcss.compile` since the real compile pipeline needs this package's production string-import loaders, which aren't wired into vitest — same reason the existing `TailwindStyle.test.tsx` mocks `tailwindCompiler` rather than exercising real output) - [x] `__tests__/components/TailwindStyle.test.tsx` — new case confirming `darkModeRootSelector` threads through to `tailwindCompiler` - [x] `npx vitest run` on both files — 9/9 passing - [x] `npx eslint` — clean - [x] `npx tsc --noEmit` — no new errors 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
rafegoldberg
pushed a commit
that referenced
this pull request
Sep 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

🎯 What does this PR do?
Fixes dark-mode CSS in
Card,Callout,Glossary, andImagebleeding in from the wrong place when they're rendered inside an app that has more than one independentdata-color-modescope on the page.The core issue: our
dark-modemixin, when used inside a nested selector, compiles to an ancestor-descendant selector —[data-color-mode='dark'] & { }. That matches when any ancestor, at any depth, carriesdata-color-mode='dark']— not the nearest one. That's fine as long as a page only ever has onedata-color-modedeclaration. It breaks once a consumer has two independent scopes that can disagree.That's exactly what happens in readme's SuperHub: the admin shell sets its own theme preference on
<html data-color-mode>, while the hub content the admin is previewing carries its own, independent preference on.rm-ReadMe[data-color-mode]. Because our selector doesn't care about proximity, aCard/Callout/Glossary/Imagerendered inside a light.rm-ReadMestill matched the dark<html>ancestor and rendered dark — an admin browsing in dark mode would see dark-mode cards and callouts even while previewing a project whose hub is set to light.The fix: a new mixin,
when-color-mode-dark($root), that anchors to a specific wrapper's own attribute (#{$root}[data-color-mode='dark'] & { }) instead of matching any ancestor — so only that one wrapper's declared mode matters, no matter what any other ancestor says.Card,Callout(all 5 variants), andImagenow use it via their existing@include;Glossary, which hand-inlined the same buggy selector, was fixed the same way directly. All four are anchored to'.rm-ReadMe', readme's hub color-mode root — see the comment onwhen-color-mode-darkfor why that's currently hardcoded (readme is this package's only real consumer today) and what to do instead if that ever stops being true. Added a warning comment to the existingdark-modemixin so this doesn't get reached for by default the next time someone adds dark-mode CSS to a component that needs to be scoped this way.Also includes two small demo-app changes:
example/Root.tsx's theme-toggle wrapper now carries the.rm-ReadMeclass, so the local demo (npm start) actually exercises the anchored selector above — without it, toggling the demo's theme selector would have silently stopped affecting these four components.example/Header.tsx/example/styles/header.scss: replaced the theme<select>dropdown with a segmented icon-button group (sun/moon/desktop), unrelated to the bug fix but bundled in since it touches the same theme-toggle UI.🧪 QA tips
npm start, open the demo, toggle the theme selector between Light / Dark / System — confirmCard,Callout(all variants), the image frame background, and the Glossary tooltip all follow the toggle correctly in each state..rm-ReadMeand<html>agree (both light or both dark) — should look identical to before.📸 Screenshot or Loom