Skip to content

ui: replace native theme select with custom floating dropdown#32148

Open
owaiss0 wants to merge 1 commit into
cloudflare:productionfrom
owaiss0:feat/theme-dropdown-ui
Open

ui: replace native theme select with custom floating dropdown#32148
owaiss0 wants to merge 1 commit into
cloudflare:productionfrom
owaiss0:feat/theme-dropdown-ui

Conversation

@owaiss0

@owaiss0 owaiss0 commented Jul 18, 2026

Copy link
Copy Markdown

Summary

This PR replaces the browser-native <select> element for theme switching with a custom floating popover dropdown UI. This update aligns the theme selector's aesthetics with the Cloudflare developers' design system and enhances interactive feedback.

🔍 Before vs After

1. Desktop Theme Selector

  • UI Control: Replaced browser-native <select> with a custom floating popover menu featuring active checkmark selection indicators.
  • Caret Icon: Replaced thin Starlight chevron with a bold PiCaretDownBold caret matching the size and weight of other header dropdowns.
  • Layout: Rebuilt with a robust flexbox layout and corrected padding offsets to eliminate icon/text overlaps.

2. Mobile Theme Selector

  • UX Integration: Integrated seamlessly into the mobile sidebar footer.
  • Positioning: Replaced the native select with a custom menu that anchors upward (bottom: 100%) on mobile viewports using CSS layout rules to prevent clipping, and automatically closes on page scroll.

🛠️ Technical Details

  • FOUC-Free Rendering: Uses a lightweight inline script running before paint to read localStorage and immediately toggle the active icons/text, querying all instances in the DOM to support multi-picker layouts.
  • Robust Keyboard Navigation (a11y): Implements full WAI-ARIA listbox behaviors. Supports arrow keys, Home/End, and Escape.
  • Focus Protection: Focus is only returned to the trigger button when closing via key commands or selection. Focus is left alone on scroll or outside-click to prevent focus-stealing.
  • Tab-out Dismissal: Closes the menu automatically when a keyboard user tabs out of the dropdown boundary using a focusout listener.
  • Memory Safety: Extracted localStorage lookups to a module-level helper, ensuring the global StarlightThemeProvider wrapper does not capture the Custom Element instance in its closure.
  • ID Collision Protection: Generates dynamic per-instance suffixes (uniqueId) for all id, aria-controls, and aria-labelledby attributes.
  • Storage Protection: All localStorage interactions are guarded with try/catch blocks for incognito/private browsing mode safety.

Documentation checklist

  • The change adheres to the documentation style guide.
  • If a larger change - such as adding a new page- an issue has been opened in relation to any incorrect or out of date information that this PR fixes.
  • Files which have changed name or location have been allocated redirects.

@owaiss0
owaiss0 requested review from a team and kodster28 as code owners July 18, 2026 02:45
@cloudflare-docs-bot

cloudflare-docs-bot Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review

⚠️ 1 warning found in commit 434c5ba.

Fix in your agent
Fix the following review findings in PR #32148 (https://github.com/cloudflare/cloudflare-docs/pull/32148).

Before making changes, review each finding and present a brief summary table:
- For each finding, state whether you agree, disagree, or need clarification
- If you disagree (e.g. the fix requires disproportionate effort for minimal benefit,
  or the finding is factually incorrect), explain why
- If you need clarification before deciding, ask those questions
- Then share your plan for which issues to tackle and in what order

After triaging, fix all legitimate findings. For any you decide to skip,
post a comment on this PR with the finding ID and your reasoning.

---

## Code Review

### Warnings (1)

#### CR-e807837aec21 · Missing accessibility UI update on initial theme apply
- **File:** `src/components/overrides/ThemeSelect.astro` line 357
- **Issue:** `applyTheme` only updates the custom picker through `updateAllThemePickers` when `StarlightThemeProvider` is absent. When the provider exists and is still unwrapped during the first `connectedCallback`, it calls the provider's `updatePickers` but never calls `this.updateUI(theme)`, leaving the active option's `aria-selected="false" even though it is visually checked.
- **Fix:** Call `this.updateUI(theme)` at the end of `applyTheme` so the custom dropdown's icon, label, checkmark, and `aria-selected` state always reflect the applied theme.

Code Review

This code review is in beta and may not always be helpful — use your judgment.

Warnings (1)
File Issue
src/components/overrides/ThemeSelect.astro line 357 Missing accessibility UI update on initial theme applyapplyTheme only updates the custom picker through updateAllThemePickers when StarlightThemeProvider is absent. When the provider exists and is still unwrapped during the first connectedCallback, it calls the provider's updatePickers but never calls this.updateUI(theme), leaving the active option's aria-selected="false" even though it is visually checked. Fix: Call this.updateUI(theme)at the end ofapplyThemeso the custom dropdown's icon, label, checkmark, andaria-selected` state always reflect the applied theme.

Conventions

No convention issues found.

Style Guide Review

No style-guide issues found.

Commands

Only codeowners can run commands. Post a comment with the command to trigger it.

Command Description
/review Runs a review now. Incremental if a prior review exists, full if not.
/full-review Re-reviews the entire PR diff from scratch, ignoring incremental history. Useful after a rebase, when you want a fresh review, or if the bot gets out of sync and reports issues that no longer exist.
/ignore-review-limit Permanently lifts the 2-review automatic limit for this PR. Future pushes will trigger reviews as normal.
/disable-auto-review Stops automatic reviews from triggering on future pushes to this PR. Codeowners can still run /review or /full-review manually.
/rebase Rebases the PR branch against production. Stops if there are conflicts and reports which files conflict.
/rebaseWithConflicts Rebases against production and attempts to resolve conflicts automatically using AI. Stops with an explanation if confidence is not high enough.

@owaiss0
owaiss0 force-pushed the feat/theme-dropdown-ui branch 4 times, most recently from 8e427bb to e25fb86 Compare July 20, 2026 05:40
@owaiss0 owaiss0 changed the title feat: replace native theme select with floating dropdown UI UI: replace native theme select with custom floating dropdown UI Jul 20, 2026
@owaiss0
owaiss0 force-pushed the feat/theme-dropdown-ui branch from e25fb86 to aecff9f Compare July 20, 2026 05:46
@owaiss0 owaiss0 changed the title UI: replace native theme select with custom floating dropdown UI ui: replace native theme select with custom floating dropdown Jul 20, 2026
@owaiss0
owaiss0 force-pushed the feat/theme-dropdown-ui branch from aecff9f to 434c5ba Compare July 20, 2026 05:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants