Skip to content

fix(ui): raise floating layers above modals so selects open in front - #2173

Open
jgt87 wants to merge 1 commit into
repowise-dev:mainfrom
jgt87:fix/select-z-index-above-dialog
Open

fix(ui): raise floating layers above modals so selects open in front#2173
jgt87 wants to merge 1 commit into
repowise-dev:mainfrom
jgt87:fix/select-z-index-above-dialog

Conversation

@jgt87

@jgt87 jgt87 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Move --z-dropdown above the surface tokens (20 → 55) so portalled floating layers paint in front of the surfaces they can be anchored inside. Fixes the Wiki style Select in the Add Repository dialog opening behind the dialog overlay.
  • Document the scale in globals.css: which layers must outrank which, and why the portalling makes DOM nesting irrelevant here.
  • Add packages/ui/__tests__/z-layering.test.ts, an ordering guard for the --z-* scale in the style of the existing token-drift.test.ts.

Related Issues

Fixes #2172

The bug

Radix portals SelectContent to document.body, so inside a Dialog it is a sibling of the dialog portal rather than a child of it. The DOM nesting that would normally settle paint order is gone, and the raw z-index values decide:

Element Class Value
DialogOverlaypackages/ui/src/ui/dialog.tsx:20 z-[var(--z-modal)] 40
DialogContentpackages/ui/src/ui/dialog.tsx:41 z-[var(--z-modal)] 40
SelectContentpackages/ui/src/ui/select.tsx:68 z-[var(--z-dropdown)] 20

The overlay's backdrop-blur-sm also establishes a containing block, which makes the occlusion total rather than merely dimmed.

It fails quietly in a way worth noting: Radix still gives the hidden listbox focus, so arrow keys and typeahead change the wiki style under a dropdown the user cannot see.

Why the token change rather than a call-site override

Passing className="z-[var(--z-modal)]" to the one <SelectContent> in add-repo-wizard.tsx is a smaller diff and fixes the reported symptom. I went the other way because Popover (popover.tsx:21) and Tooltip (tooltip.tsx:20) carry the same --z-dropdown and would fail identically the first time either is rendered inside a Dialog — the point-fix leaves that armed.

55 rather than 45, because the command palette hardcodes z-[calc(var(--z-modal) + 1)] (packages/web/src/components/search/command-palette.tsx:105) instead of reading --z-command. A dropdown that only cleared --z-modal would still open behind the palette.

Happy to swap to the narrow fix if you'd prefer to keep the scale as-is.

On the test

No render test can catch this: jsdom has no paint and no stacking contexts, and the wizard's existing test renders the Select happily today. So the guard asserts the ordering of the scale itself, and additionally pins the components to the tokens it sorts — an ordered scale means nothing once a portalled layer is switched to a bare z-50.

Verified it fails before the fix:

- []
+ [
+   "--z-dropdown (20) must be above --z-sidebar (30)",
+   "--z-dropdown (20) must be above --z-modal (40)",
+   "--z-dropdown (20) must be above --z-command (50)",
+ ]
AssertionError: expected 20 to be greater than 41

Out of scope, but noticed

--z-base, --z-elevated, --z-sidebar and --z-command currently have no call sites — the palette offsets --z-modal directly instead of using --z-command. Left alone here; happy to fold a cleanup into a separate PR if useful.

Test Plan

  • Web UI type check — npm run type-check, exit 0
  • Web UI lint — npm run lint, exit 0 (one pre-existing warning in coverage/freshness-table-wrapper.tsx, untouched here)
  • Web build passes — npm run build, exit 0 (Next.js build + VS Code extension bundle)
  • Lint passes — uv run ruff check ., "All checks passed!"
  • New test fails before the fix and passes after; token-drift, brand and add-repo-wizard tests pass alongside it
  • pytest — not completed locally (still running when this was opened). This change touches no Python; deferring to CI's 3.11/3.12/3.13 jobs.

One note on the packages/ui vitest suite: it is flaky on my machine independently of this change — 14 failures on a clean main checkout, then 12 and 13 across two runs with this patch applied, with the failing sets differing between runs. Total test count moves 1671 → 1676, exactly the five added here. All tests this change could plausibly affect pass in isolation. Flagging in case that is known; I have not investigated it and it is not addressed by this PR.

Checklist

  • My code follows the project's code style
  • I have added tests for new functionality
  • All existing tests still pass (subject to the pre-existing flakiness noted above)
  • I have updated documentation if needed (the token block is now commented; no other docs reference the z scale)

The Wiki style select in the Add Repository dialog opened behind the
dialog. Radix portals SelectContent to document.body, so inside a Dialog
it is a sibling of the dialog portal rather than a child of it; the DOM
nesting that would normally settle paint order is gone and the raw
z-index values decide it instead. --z-dropdown was 20 against --z-modal's
40, so the listbox painted under the overlay, and the overlay's
backdrop-blur-sm containing block made the occlusion total rather than
merely dimmed.

The failure is quiet in a way that matters. Radix still gives the hidden
listbox focus, so arrow keys and typeahead change the wiki style under a
dropdown nobody can see.

Fixed on the scale rather than at the call site. --z-dropdown moves to 55:
above every surface it can be anchored inside, below --z-toast. A
className override on the single SelectContent in add-repo-wizard.tsx
would have fixed the reported symptom with a smaller diff, but Popover and
Tooltip carry the same --z-dropdown and would fail identically the first
time either is rendered inside a Dialog, so the token block now says which
layers must outrank which and why. 55 rather than 45 because the command
palette hardcodes calc(var(--z-modal) + 1) instead of reading --z-command;
a dropdown that only cleared --z-modal would still open behind the palette.

No render test could have caught this. jsdom has no paint and no stacking
contexts, and the wizard's existing test renders the Select happily today.
So the new guard asserts the ordering of the scale itself, and pins the
components to the tokens it sorts, since an ordered scale means nothing
once a portalled layer is switched to a bare z-50.

Fixes repowise-dev#2172
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.

[Bug] Wiki style dropdown in the Add Repository dialog renders behind the modal

1 participant