Skip to content

fix: report the active theme in the toggle menu - #56

Merged
ryaneggz merged 1 commit into
developmentfrom
fix/theme-toggle-state-aria
Aug 3, 2026
Merged

fix: report the active theme in the toggle menu#56
ryaneggz merged 1 commit into
developmentfrom
fix/theme-toggle-state-aria

Conversation

@ryaneggz

@ryaneggz ryaneggz commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Stacked on #54. Base is fix/theme-auto-os-dark-and-cross-tab because both PRs touch mode-toggle.tsx. GitHub will retarget this to development automatically when #54 merges. Review that one first.

The theme menu offered three destinations and reported no state.

DropdownMenuItem carries role="menuitem" with no checked semantics, and the trigger's accessible name was the constant "Toggle theme". So there was nowhere in the website UI that reported which theme is active. Sighted users read it off the page; screen-reader users had nothing.

Slightly ironic against the console, which already exposes aria-pressed and a literal "On"/"Off" in its authenticated menu.

Fix

DropdownMenuRadioGroup / DropdownMenuRadioItem — both already exported by the design system (ui/dropdown-menu.tsx:19,120) and used nowhere. Each option now reports role="menuitemradio" with aria-checked, and gets the existing ItemIndicator dot for free.

The trigger names the current selection. "System" alone still doesn't tell you whether the page is light or dark right now, so that case resolves: Theme: System (currently dark).

This is pre-existing debt

git show 2b66e7a -- mode-toggle.tsx shows the time-of-day change only wrapped the onClick handlers. The plain-item structure predates it and was untouched. It ships here because it is the same control, not because that feature caused it.

Hydration

The state-dependent name is gated behind a mounted flag. The active theme is unknowable during SSR and is resolved from the DOM on the client, so rendering it before mount would hydrate against markup the server could not have produced.

Verified against the built HTML: it contains Toggle theme and zero occurrences of Theme: .

Verification

Accessibility properties read from a live browser, not from source.

State Trigger accessible name Menu
automatic default, 16:00 local Theme: Light menuitemradio ×3 — aria-checked = true / false / false
after choosing System Theme: System (currently light) theme-choice: system persisted
System, OS flipped to dark Theme: System (currently dark) page follows — correct, the user asked for it

Note on the automatic default

Under the automatic default the menu shows Light or Dark checked, because that is genuinely the active theme. It does not claim the user chose it — nothing is persisted until they do.

@ryaneggz
ryaneggz changed the base branch from fix/theme-auto-os-dark-and-cross-tab to development August 3, 2026 00:23
The theme menu offered three destinations and reported no state. Plain
DropdownMenuItems carry role="menuitem" with no checked semantics, and the
trigger's accessible name was the constant "Toggle theme" - so there was
nowhere in the website UI that told a non-sighted user which theme was active.
Sighted users read it off the page itself; screen-reader users had nothing.

Switched to DropdownMenuRadioGroup/RadioItem, which the design system already
exports and which nothing was using. Each option now reports
role="menuitemradio" with aria-checked, and the trigger names the current
selection - including which way System currently resolves, since "System" alone
still does not say whether the page is light or dark right now.

This is pre-existing debt, not fallout from the time-of-day default: the
timezone change only wrapped the onClick handlers, and left the plain-item
structure untouched. It is fixed here because it is the same control.

The state-dependent name is gated behind a mounted flag. The active theme is
unknowable during SSR and is resolved from the DOM on the client, so rendering
it before mount would hydrate against markup the server could not produce. The
server-rendered name stays "Toggle theme" - verified: the built HTML contains
that string and zero occurrences of "Theme: ".

Verified in a browser by reading the accessibility properties, not the source:
role=menuitemradio on all three with aria-checked true/false/false; trigger
"Theme: Light" under the automatic default, "Theme: System (currently light)"
after choosing System, and "Theme: System (currently dark)" once the OS
preference flips - which is the one case where following the OS live is
correct, because the user asked for it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ryaneggz
ryaneggz force-pushed the fix/theme-toggle-state-aria branch from 6aa2493 to 78bd428 Compare August 3, 2026 00:24
@ryaneggz
ryaneggz merged commit 94f267f into development Aug 3, 2026
@ryaneggz
ryaneggz deleted the fix/theme-toggle-state-aria branch August 3, 2026 00:24
@netlify

netlify Bot commented Aug 3, 2026

Copy link
Copy Markdown

Deploy Preview for promptengineers ready!

Name Link
🔨 Latest commit 78bd428
🔍 Latest deploy log https://app.netlify.com/projects/promptengineers/deploys/6a6fdfa63e584500086701ee
😎 Deploy Preview https://deploy-preview-56--promptengineers.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

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.

1 participant