TanStack

Overlay Audit

Every dialog, drawer and overlay currently shipping on the site, copied verbatim into isolated specimens so they can be compared side by side. This page is scaffolding for designing the DS overlay primitive — it is not itself part of the system, and nothing here should be imported outside /ds.

Launch

Open each specimen against the same background. Grouped by positional posture — the axis that actually needs to become a prop.

Centered6 variants

Edge sheet1 variant

Anchored panel1 variant

Bottom sheet1 variant

Top anchored1 variant

Full bleed1 variant

Property matrix

Read off the source, not inferred. The four columns on the right are what Radix gives you for free — every ✗ is a hand-rolled dialog missing a behaviour it should never have had to implement.

SpecimenPostureBaseTokensz-indexScrimTrapRestoreEscLockAnim

components/LoginModal.tsx · 73 lines

Centeredradixraw-tailwind999 / 1000bg-black/60 backdrop-blur-sm

components/AvatarCropModal.tsx · 176 lines

Centeredradixraw-tailwind999 / 1000bg-black/60 backdrop-blur-sm

routes/stats/npm/index.tsx · 40 lines

Centeredradixraw-tailwind999 / 1000bg-black/60 backdrop-blur-sm

routes/admin/roles.$roleId.tsx · 20 lines

Centeredhand-rolledraw-tailwind50bg-black/50 (no blur)

components/ExampleDeployDialog.tsx · 480 lines

Centeredhand-rolledraw-tailwind50bg-black/50 backdrop-blur-sm

components/application-starter/DeployDialog.tsx · 585 lines

Centeredhand-rolledraw-tailwind50bg-black/50 backdrop-blur-sm

components/charts/BuilderGuideDialog.tsx · 150 lines

Edge sheetradixsemantic999 / 1000bg-black/45 backdrop-blur-[1px]

components/shop/CartDrawer.tsx · 257 lines

Anchored panelradixshop-scope100bg-black/40 (no blur)

components/shop/ProductDrawer.tsx · 700 lines

Bottom sheethand-rolledshop-scope60 / 70 / 71bg-black/50 backdrop-blur-sm

components/LibrariesOverlay.tsx · 57 lines

Full bleedradixsemantic110 / 111 / 112bespoke .libraries-overlay-glass

components/SearchModal.tsx · 3766 lines

Top anchoredradixraw-tailwind999 / 1000bg-black/60 → xl:bg-black/30

Divergence

Where the eleven specimens disagree. Each row is a decision the primitive has to make once, so eleven call sites stop making it independently.

z-index tier

5 distinct values
5060/70/71100110/111/112999/1000

Five unrelated stacking families. Now a documented scale — --z-scrim, --z-overlay, and --z-above-overlay for chrome that must float over an open overlay. No overlay declares its own tier any more.

Scrim

7 distinct values
black/40 no blurblack/50 no blurblack/50 blur-smblack/45 blur-[1px]black/60 blur-smblack/60→30 responsivebespoke glass

Seven scrims. Now two, as recommended: --color-scrim for panels, and the glass treatment as Takeover's immersive variant. Only SearchModal still declares its own.

Base

2 distinct values
Radix (7)hand-rolled (4)

The 4 hand-rolled ones accounted for every a11y failure in the audit. All four are now migrated — every overlay on the site is Radix-backed.

Token layer

3 distinct values
semantic (2)raw tailwind (7)shop-scope (2)

Only BuilderGuideDialog and LibrariesOverlay are on the semantic layer.

Animation

3 distinct values
real keyframes (3)dead animate-in classes (1)none (7)

tailwindcss-animate is NOT installed, so every `animate-in` / `fade-in-0` / `zoom-in-95` class in the codebase is inert. Only LibrariesOverlay, SearchModal and ProductDrawer actually move.

Corner radius

4 distinct values
rounded-lgrounded-xlrounded-t-2xlrounded-none (sm)

rounded-xl is the de facto standard; one outlier at lg.

Notes per specimen

What each one contributes to the primitive's requirements.

LoginModal
The baseline centered panel. Identical shell to AvatarCropModal and the npm stats dialog apart from max-width.
AvatarCropModal
Adds a footer action row (Cancel / Save) that no other centered dialog shares — a footer slot the primitive needs.
npm stats dialog
A fourth copy of the same shell, declared inline in a route file. Adds a mobile gutter (w-[calc(100%-1rem)]) the other three lack.
Roles confirm
Worst case. No portal, no dialog role, no close button, no Escape. A destructive confirm that a keyboard user cannot dismiss.
ExampleDeployDialog
Tinted header + 5-step wizard body. Shell is byte-identical to StarterDeployDialog.
Starter DeployDialog
The twin. Same shell, same wizard, different provider branding. Strongest extraction case in the audit.
BuilderGuideDialog
Strongest on tokens and layout — semantic throughout, fixed header over an independently scrolling body, responsive posture change. But its `animate-in / fade-in-0 / slide-in-from-right` classes match ZERO css rules: no tailwindcss-animate plugin is installed. It appears animated in source and is not.
CartDrawer
MIGRATED — now Drawer side="right" size="sm" fit anchor="navbar". Its navbar offset became the DS `anchor` prop, which is the whole of this posture. Keeps its own surface colours by passing shop-scope through className: the primitive supplies posture and behaviour, the shop supplies the palette.
ProductDrawer
MIGRATED — now Drawer side="bottom" fit. Was the last hand-rolled overlay on the site; Radix now supplies the focus trap, focus restoration, Escape and scroll lock it never had. Its prev/next arrows moved from viewport-fixed on a third z-tier to panel-absolute, because Radix traps focus inside the panel and viewport-level siblings would have been unreachable by keyboard.
LibrariesOverlay
MIGRATED — now Takeover with scrim="glass". Its bespoke glass treatment became the DS's second scrim, which is the pair the audit asked for after finding seven. LibrariesBrowser also dropped its direct Radix import in favour of TakeoverTitle / TakeoverDescription.
SearchModal
TOKENS ADOPTED, POSTURE NOT EXTRACTED. Now on bg-scrim and the --z-scrim / --z-overlay / --z-above-overlay tiers, which closes the last bespoke values in the audit. The shell itself stays: it is already Radix, already accessible, and already has real data-state animation, so extracting a CommandPalette buys no correctness — and it would need five caller-specific escape hatches (forceMount on Portal/Overlay/Content to keep InstantSearch state alive; animation on an inner panel because Content is a full-bleed hit area on mobile; the top-anchored responsive posture; a conditional sm:bottom-4; and a scrim that lightens at xl). One caller is below the extraction threshold. Revisit if a second command palette appears.