Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,20 @@
--space-2xl: 3rem; /* 48px */
--space-3xl: 4rem; /* 64px */

/* Z-index scale — single source of truth */
/* Z-index scale — single source of truth.
* Audit Sprint 6b (2026-05-23): the scale existed but adoption was
* spotty (most modal surfaces still hardcoded z-50, command palette
* z-50, cookie consent z-[60]). New rule: every fixed/absolute
* stacked element references a token here. Order is bottom → top:
* - dropdown/sticky/nav: page chrome
* - modal-backdrop/modal: app dialogs
* - popover/tooltip: rare anchored layers
* - overlay: cookie consent, generic full-bleed callouts
* - tour: product-tour overlays (must sit above modals so a tour
* can call out a modal)
* - toast: sonner notifications must be ABOVE tour so a critical
* toast that fires during a tour is still visible
* - debug: dev-only indicators */
--z-base: 1;
--z-dropdown: 10;
--z-sticky: 20;
Expand All @@ -39,6 +52,7 @@
--z-tooltip: 80;
--z-overlay: 90;
--z-tour: 100;
--z-toast: 110;
--z-debug: 120;

/* Motion tokens — mirrors config/motion.ts */
Expand Down
5 changes: 4 additions & 1 deletion components/CookieConsent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ export default function CookieConsent() {
// Not aria-modal — we don't want to trap focus; we want the
// banner to be the FIRST natural stop after Skip-to-main so a
// keyboard user can dismiss it without tabbing through the page.
className="fixed inset-x-0 bottom-0 z-[60] px-4 pb-4 sm:px-6 sm:pb-6"
// Audit Sprint 6b: was z-[60] hardcoded. Use --z-overlay (90) so
// the banner sits above page chrome but below modals + tour + toast.
style={{ zIndex: 'var(--z-overlay)' }}
className="fixed inset-x-0 bottom-0 px-4 pb-4 sm:px-6 sm:pb-6"
>
<div className="mx-auto max-w-4xl rounded-2xl border border-slate-700/80 bg-slate-900/95 p-4 shadow-2xl backdrop-blur supports-[backdrop-filter]:bg-slate-900/80 sm:p-5">
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
Expand Down
9 changes: 7 additions & 2 deletions components/command-palette/CommandPalette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,11 @@ export function CommandPalette() {
animate="visible"
exit="exit"
transition={{ duration: 0.15 }}
className="fixed inset-0 z-50 bg-black/60 backdrop-blur-sm"
// Audit Sprint 6b: token-scaled. Command palette is a
// top-level modal; uses the same backdrop/content tokens
// as the shared dialog primitive.
style={{ zIndex: 'var(--z-modal-backdrop)' }}
className="fixed inset-0 bg-black/60 backdrop-blur-sm"
onClick={() => setOpen(false)}
/>

Expand All @@ -513,7 +517,8 @@ export function CommandPalette() {
role="dialog"
aria-modal="true"
aria-label="Command palette"
className="fixed inset-0 z-50 flex items-start justify-center px-4 pt-[15vh] sm:pt-[20vh]"
style={{ zIndex: 'var(--z-modal)' }}
className="fixed inset-0 flex items-start justify-center px-4 pt-[15vh] sm:pt-[20vh]"
>
<Command
className="w-full max-w-[640px] overflow-hidden rounded-2xl border border-glass-border bg-background/95 shadow-[0_24px_80px_rgba(0,0,0,0.5),0_0_0_1px_rgba(255,255,255,0.05)] backdrop-blur-xl"
Expand Down
7 changes: 5 additions & 2 deletions components/ui/alert-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ function AlertDialogOverlay({
return (
<AlertDialogPrimitive.Overlay
data-slot="alert-dialog-overlay"
// Audit Sprint 6b: token-scaled (see app/globals.css).
style={{ zIndex: 'var(--z-modal-backdrop)' }}
className={cn(
"fixed inset-0 z-50 bg-black/60 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
"fixed inset-0 bg-black/60 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className,
Comment on lines +42 to 46
)}
{...props}
Expand All @@ -57,8 +59,9 @@ function AlertDialogContent({
<AlertDialogOverlay />
<AlertDialogPrimitive.Content
data-slot="alert-dialog-content"
style={{ zIndex: 'var(--z-modal)' }}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-md translate-x-[-50%] translate-y-[-50%] gap-4 border border-glass-border bg-slate-900 p-6 shadow-xl rounded-lg",
"fixed left-[50%] top-[50%] grid w-full max-w-md translate-x-[-50%] translate-y-[-50%] gap-4 border border-glass-border bg-slate-900 p-6 shadow-xl rounded-lg",
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
className,
)}
Expand Down
9 changes: 7 additions & 2 deletions components/ui/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,12 @@ function DialogOverlay({
return (
<DialogPrimitive.Overlay
data-slot="dialog-overlay"
// Audit Sprint 6b: token-scaled via inline style so the value
// resolves from app/globals.css and stays consistent across
// every modal surface in the app.
style={{ zIndex: 'var(--z-modal-backdrop)' }}
className={cn(
"fixed inset-0 z-50 bg-black/60 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
"fixed inset-0 bg-black/60 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className,
)}
Comment on lines +50 to 54
{...props}
Expand All @@ -66,8 +70,9 @@ function DialogContent({
<DialogOverlay />
<DialogPrimitive.Content
data-slot="dialog-content"
style={{ zIndex: 'var(--z-modal)' }}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-glass-border bg-slate-900 p-6 shadow-xl rounded-lg",
"fixed left-[50%] top-[50%] grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-glass-border bg-slate-900 p-6 shadow-xl rounded-lg",
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
className,
)}
Expand Down
7 changes: 5 additions & 2 deletions components/ui/sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ function SheetOverlay({
return (
<SheetPrimitive.Overlay
data-slot="sheet-overlay"
// Audit Sprint 6b: token-scaled to match dialog/alert-dialog.
style={{ zIndex: 'var(--z-modal-backdrop)' }}
className={cn(
"fixed inset-0 z-50 bg-black/60 backdrop-blur-sm",
"fixed inset-0 bg-black/60 backdrop-blur-sm",
className
Comment on lines +31 to 35
)}
{...props}
Expand All @@ -50,8 +52,9 @@ function SheetContent({
<SheetOverlay />
<SheetPrimitive.Content
data-slot="sheet-content"
style={{ zIndex: 'var(--z-modal)' }}
className={cn(
"fixed z-50 flex flex-col gap-4 bg-glass-strong shadow-xl transition ease-in-out overflow-y-auto",
"fixed flex flex-col gap-4 bg-glass-strong shadow-xl transition ease-in-out overflow-y-auto",
side === "right" &&
"inset-y-0 right-0 h-full w-[90vw] max-w-[360px] border-l border-glass-border sm:w-3/4 sm:max-w-sm",
side === "left" &&
Expand Down
6 changes: 6 additions & 0 deletions components/ui/toaster.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,19 @@

import { Toaster as SonnerToaster } from "sonner";

// Audit Sprint 6b (2026-05-23): toasts must sit above tour overlays
// (--z-tour=100) so a critical toast firing during a product tour
// is still visible. --z-toast=110 added to app/globals.css for this.
const TOAST_OFFSET_STYLE: React.CSSProperties = { zIndex: 'var(--z-toast)' };

export function Toaster() {
return (
<SonnerToaster
position="bottom-right"
richColors={false}
closeButton
theme="dark"
style={TOAST_OFFSET_STYLE}
toastOptions={{
className: "border border-glass-border bg-slate-900 text-slate-100",
}}
Expand Down