Skip to content

Bug: dark theme broken on contributor-status tracked-library badges (regression from #73) #77

@TheSeydiCharyyev

Description

@TheSeydiCharyyev

Description

After PR #73 merged, the tracked-library badges on /profile/contributor-status are unreadable in dark theme — dark-green text on a dark background. The light-theme fix from #73 works correctly, but dark theme is now broken.

Root cause looks structural: the repo never declared a class-based dark variant for Tailwind CSS 4, so dark:* modifiers (including the ones I added in #73) only activate via @media (prefers-color-scheme: dark), not via the .dark class that the in-app theme toggle uses. Users whose OS is in light mode and who toggle dark theme inside the app never get the dark: overrides.

Full diagnosis and three possible fix paths are in the Additional Context section below.

Steps to Reproduce

  1. Make sure your OS color scheme is set to light (Windows: Settings → Personalization → Colors → Light, macOS: System Settings → Appearance → Light)
  2. Open https://react.foundation/profile/contributor-status while signed in
  3. Use the header theme toggle to switch the app to dark theme
  4. Scroll to "Your Contributions" → expand → "Tracked Libraries"
  5. Look at the badges for libraries with contributions (e.g. storybookjs/storybook, pmndrs/zustand, expo/expo, TanStack/query) and at their contribution-count chips

Expected Behavior

Tracked-library badges remain readable in both themes regardless of the user's OS color-scheme preference. The in-app theme toggle should drive badge text colors via the .dark class on <html>.

Actual Behavior

In dark theme with OS=light, the contributed-library badge text and the contribution-count chip render in text-emerald-700 / text-emerald-800 (dark green) on the dark page background — effectively invisible. The library names and counts cannot be read.

Light theme (OS=light) renders correctly thanks to PR #73. Dark theme with OS=dark also renders correctly because the media-query-based dark: variant activates from OS preference.

Only the OS=light + app=dark combination is broken, but that's a common combination (users who prefer dark UI but keep a light OS).

Environment

Production https://react.foundation, Chrome on Windows 11 (OS color scheme = light), in-app theme toggle set to dark. Verified 2026-06-03, after PR #73 (commit 51ed02a) shipped to main.

Additional Context

Diagnosis

src/app/globals.css does not declare a class-based dark variant for Tailwind CSS 4. There is no @custom-variant dark (&:where(.dark, .dark *)); (or equivalent @variant dark / legacy darkMode config) anywhere in the repo:

grep -rn "@custom-variant\|@variant dark\|darkMode" .   # no matches

In Tailwind 4 the default dark variant compiles to @media (prefers-color-scheme: dark), so every dark:* modifier in the codebase only activates from the OS preference, not from the .dark class that ThemeProvider toggles on <html>.

The rest of the app survives this because most dark-mode behavior is driven through the semantic CSS variables in :root / .dark (--background, --foreground, etc.) and the prose styles in globals.css use hand-written html.dark .prose … selectors instead of dark:* utilities.

Why this regression appeared after #73

Before #73:

After #73:

  • Badge text: text-emerald-700 dark:text-emerald-200. The dark:text-emerald-200 override never kicks in for users with OS=light, so the base text-emerald-700 (dark green) leaks into dark theme — invisible on dark.

The fix in #73 was correct in intent but assumed dark: was wired to the .dark class. It is not.

The same pattern exists in other files

A quick grep shows several other components using dark:* modifiers that would silently misbehave for OS=light + app=dark users:

  • src/components/communities/VerificationBadge.tsx
  • src/components/admin/LibraryDetailModal.tsx
  • src/components/admin/LibraryEligibilityEditor.tsx
  • src/app/admin/ingest/inspect/page.tsx
  • src/app/admin/ingest-full/page.tsx
  • src/app/admin/import/page.tsx
  • src/app/communities/[slug]/page.tsx

These mostly hide the issue because their backgrounds use semantic tokens, but the foreground text colors are still wrong in the OS=light + app=dark combination.

Possible fix paths

(A) Wire dark: to the .dark class (one-line repo-wide fix). Add to globals.css:

@custom-variant dark (&:where(.dark, .dark *));

Smallest diff, fixes the regression in #73 and also restores the intended behavior for every other dark:* modifier in the repo. May surface previously-hidden visual differences in components that have been relying on the broken behavior — worth a quick visual check on the affected files above.

(B) Replace dark:text-emerald-* in tracked-library-badge.tsx with semantic tokens. Smallest scope, only touches the component from #73. The challenge: text-success resolves to --success (emerald-500) which is identical in both themes, and on bg-success/15 in light mode the contrast against the pale-emerald background sits around the WCAG AA edge for normal text. Workable but needs a quick contrast pass.

(C) Surgical CSS variables for the badge text. Define --badge-contributed-text and --badge-count-text in :root and .dark, then use text-[hsl(var(--badge-contributed-text))] in the component. Localized and theme-correct, but adds new variables for a single component.

Question for routing

@dougbot-agent — flagging this as a regression from PR #73. (A) is the structural fix and would also clear the silent issues in the other components listed above; (B) and (C) are scoped purely to the #73 surface. Which direction is preferred here? Happy to draft the PR once we agree on scope.

Screenshots showing the broken dark theme and the working light theme are attached below.

Image Image

Acceptance Criteria

No response

Non-goals

No response

Metadata

Metadata

Assignees

Labels

priority:P1High prioritystatus:in-progressImplementation is actively in progresstype:bugBug or incorrect behavior

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions