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.
| Specimen | Posture | Base | Tokens | z-index | Scrim | Trap | Restore | Esc | Lock | Anim |
|---|---|---|---|---|---|---|---|---|---|---|
components/LoginModal.tsx · 73 lines | Centered | radix | raw-tailwind | 999 / 1000 | bg-black/60 backdrop-blur-sm | |||||
components/AvatarCropModal.tsx · 176 lines | Centered | radix | raw-tailwind | 999 / 1000 | bg-black/60 backdrop-blur-sm | |||||
routes/stats/npm/index.tsx · 40 lines | Centered | radix | raw-tailwind | 999 / 1000 | bg-black/60 backdrop-blur-sm | |||||
routes/admin/roles.$roleId.tsx · 20 lines | Centered | hand-rolled | raw-tailwind | 50 | bg-black/50 (no blur) | |||||
components/ExampleDeployDialog.tsx · 480 lines | Centered | hand-rolled | raw-tailwind | 50 | bg-black/50 backdrop-blur-sm | |||||
components/application-starter/DeployDialog.tsx · 585 lines | Centered | hand-rolled | raw-tailwind | 50 | bg-black/50 backdrop-blur-sm | |||||
components/charts/BuilderGuideDialog.tsx · 150 lines | Edge sheet | radix | semantic | 999 / 1000 | bg-black/45 backdrop-blur-[1px] | |||||
components/shop/CartDrawer.tsx · 257 lines | Anchored panel | radix | shop-scope | 100 | bg-black/40 (no blur) | |||||
components/shop/ProductDrawer.tsx · 700 lines | Bottom sheet | hand-rolled | shop-scope | 60 / 70 / 71 | bg-black/50 backdrop-blur-sm | |||||
components/LibrariesOverlay.tsx · 57 lines | Full bleed | radix | semantic | 110 / 111 / 112 | bespoke .libraries-overlay-glass | |||||
components/SearchModal.tsx · 3766 lines | Top anchored | radix | raw-tailwind | 999 / 1000 | bg-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 values5060/70/71100110/111/112999/1000Five 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 valuesblack/40 no blurblack/50 no blurblack/50 blur-smblack/45 blur-[1px]black/60 blur-smblack/60→30 responsivebespoke glassSeven 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 valuesRadix (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 valuessemantic (2)raw tailwind (7)shop-scope (2)Only BuilderGuideDialog and LibrariesOverlay are on the semantic layer.
Animation
3 distinct valuesreal 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 valuesrounded-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.