mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-29 18:46:59 +00:00
refactor(desktop): name the overlay z-index ladder
DESIGN.md already said app-wide surfaces must not compete through ad-hoc
z-index literals, and the code disagreed: three overlapping numbering
schemes, and comments narrating the fight ("defaults to z-130, renders
UNDER the onboarding overlay (z-1300) ... bump it above with z-[1310]").
Picking a number meant reading someone else's near miss.
Name the rungs — modal, over-modal, switcher, and the boot chain — and
point the call sites at them. Every rung keeps the exact value it had, so
nothing moves; what changes is that the next overlay has a name to reach
for instead of a number to guess. Local stacking within a component stays
on plain z-10/z-20.
This commit is contained in:
parent
9ae3bd73c9
commit
c7ef4c192d
20 changed files with 67 additions and 41 deletions
|
|
@ -192,7 +192,12 @@ Notes:
|
|||
semantics when unifying appearance.
|
||||
- Respect `AppShell` overlay ownership. Persistent terminal/content layers,
|
||||
route overlays, dialogs, and boot surfaces must not compete through ad-hoc
|
||||
z-index literals.
|
||||
z-index literals. Pick a rung of the ladder in `styles.css` instead —
|
||||
`--z-modal-backdrop` / `--z-modal` / `--z-modal-popover`, `--z-over-modal`
|
||||
(toasts, tooltips, command surfaces) and `--z-over-modal-content`,
|
||||
`--z-switcher-backdrop` / `--z-switcher`, then the boot chain
|
||||
`--z-connecting` → `--z-onboarding` → `--z-setup` → `--z-crash`. Plain
|
||||
`z-10`/`z-20` are still right for stacking *within* one component.
|
||||
|
||||
## Iconography & brand
|
||||
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ export function BaseBranchPicker({
|
|||
<span className="shrink-0">{parts.after}</span>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent align="start" className="z-[140] min-w-(--radix-popover-trigger-width) p-0">
|
||||
<PopoverContent align="start" className="z-(--z-modal-popover) min-w-(--radix-popover-trigger-width) p-0">
|
||||
<Command filter={(searchValue, search) => (searchValue.toLowerCase().includes(search.toLowerCase()) ? 1 : 0)}>
|
||||
<CommandInput autoFocus placeholder={p.baseBranchPlaceholder} />
|
||||
<CommandList className="max-h-64">
|
||||
|
|
|
|||
|
|
@ -875,13 +875,13 @@ export function CommandPalette() {
|
|||
<DialogPrimitive.Root onOpenChange={setCommandPaletteOpen} open={open}>
|
||||
<DialogPrimitive.Portal>
|
||||
{/* Transparent overlay: keeps click-away + focus trap, but no dim/blur. */}
|
||||
<DialogPrimitive.Overlay className="fixed inset-0 z-[200]" />
|
||||
<DialogPrimitive.Overlay className="fixed inset-0 z-(--z-over-modal)" />
|
||||
<DialogPrimitive.Content
|
||||
aria-describedby={undefined}
|
||||
className={cn(
|
||||
HUD_POSITION,
|
||||
HUD_SURFACE,
|
||||
'z-[210] w-[min(34rem,calc(100vw-2rem))] overflow-hidden duration-150 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:slide-in-from-top-2 data-[state=open]:zoom-in-95'
|
||||
'z-(--z-over-modal-content) w-[min(34rem,calc(100vw-2rem))] overflow-hidden duration-150 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:slide-in-from-top-2 data-[state=open]:zoom-in-95'
|
||||
)}
|
||||
>
|
||||
<DialogPrimitive.Title className="sr-only">{t.commandCenter.paletteTitle}</DialogPrimitive.Title>
|
||||
|
|
|
|||
|
|
@ -31,9 +31,9 @@ export function ProviderPicker() {
|
|||
<ChevronDown className="size-3" />
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
{/* The picker lives inside the pet-gen Dialog (z-130) and portals to body,
|
||||
so lift its menu above the dialog or it opens behind it. */}
|
||||
<DropdownMenuContent align="start" className="z-[140]">
|
||||
{/* The picker lives inside the pet-gen Dialog and portals to body, so its
|
||||
menu needs the rung above the modal or it opens behind the dialog. */}
|
||||
<DropdownMenuContent align="start" className="z-(--z-modal-popover)">
|
||||
{providers.map(provider => (
|
||||
<DropdownMenuItem
|
||||
className="flex items-center gap-1.5"
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ export function SessionSwitcher() {
|
|||
<>
|
||||
{/* Transparent click-catcher: click-away closes, but no dim/blur. */}
|
||||
<div
|
||||
className="fixed inset-0 z-[219]"
|
||||
className="fixed inset-0 z-(--z-switcher-backdrop)"
|
||||
onMouseDown={e => {
|
||||
e.preventDefault()
|
||||
closeSwitcher()
|
||||
|
|
@ -56,7 +56,7 @@ export function SessionSwitcher() {
|
|||
className={cn(
|
||||
HUD_POSITION,
|
||||
HUD_SURFACE,
|
||||
'dt-portal-scrollbar z-[220] max-h-[min(22rem,64vh)] w-[min(19rem,calc(100vw-2rem))] select-none overflow-y-auto p-1'
|
||||
'dt-portal-scrollbar z-(--z-switcher) max-h-[min(22rem,64vh)] w-[min(19rem,calc(100vw-2rem))] select-none overflow-y-auto p-1'
|
||||
)}
|
||||
>
|
||||
{sessions.map((session, i) => {
|
||||
|
|
|
|||
|
|
@ -284,7 +284,7 @@ export function BootFailureOverlay() {
|
|||
|
||||
if (view === 'connect') {
|
||||
return (
|
||||
<div className="fixed inset-0 z-[1400] flex items-center justify-center bg-(--ui-chat-surface-background) p-6">
|
||||
<div className="fixed inset-0 z-(--z-setup) flex items-center justify-center bg-(--ui-chat-surface-background) p-6">
|
||||
<div className="flex max-h-[86vh] w-full max-w-[46rem] flex-col overflow-hidden rounded-xl border border-(--stroke-nous) bg-(--ui-chat-bubble-background) shadow-nous">
|
||||
{/* Subtle back affordance (projects/overlay idiom): muted → foreground
|
||||
on hover, no divider. */}
|
||||
|
|
@ -307,7 +307,7 @@ export function BootFailureOverlay() {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-[1400] flex items-center justify-center bg-(--ui-chat-surface-background) p-6">
|
||||
<div className="fixed inset-0 z-(--z-setup) flex items-center justify-center bg-(--ui-chat-surface-background) p-6">
|
||||
<div className="w-full max-w-[40rem] overflow-hidden rounded-xl border border-(--stroke-nous) bg-(--ui-chat-bubble-background) shadow-nous">
|
||||
<div className="flex items-start gap-3 px-5 py-4">
|
||||
<ErrorIcon className="mt-0.5" size="1.25rem" />
|
||||
|
|
|
|||
|
|
@ -398,7 +398,7 @@ export function DesktopInstallOverlay({ enabled = true }: DesktopInstallOverlayP
|
|||
|
||||
if (state.setupChoice) {
|
||||
return (
|
||||
<div className="fixed inset-0 z-[1400] flex items-center justify-center bg-background/90 p-4 backdrop-blur-md">
|
||||
<div className="fixed inset-0 z-(--z-setup) flex items-center justify-center bg-background/90 p-4 backdrop-blur-md">
|
||||
<div className="w-full max-w-2xl rounded-xl border border-(--stroke-nous) bg-card p-8 shadow-nous">
|
||||
<div className="flex items-start gap-4">
|
||||
<BrandMark className="size-11 shrink-0" />
|
||||
|
|
@ -478,7 +478,7 @@ export function DesktopInstallOverlay({ enabled = true }: DesktopInstallOverlayP
|
|||
const platformLabel = ups.platform === 'darwin' ? 'macOS' : ups.platform === 'linux' ? 'Linux' : ups.platform
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-[1400] flex items-center justify-center bg-background/90 backdrop-blur-md">
|
||||
<div className="fixed inset-0 z-(--z-setup) flex items-center justify-center bg-background/90 backdrop-blur-md">
|
||||
<div className="w-full max-w-xl rounded-xl border border-(--stroke-nous) bg-card p-8 shadow-nous">
|
||||
<h2 className="text-xl font-semibold tracking-tight">{copy.oneTimeTitle}</h2>
|
||||
<p className="mt-2 text-sm text-muted-foreground">{copy.unsupportedDesc(platformLabel)}</p>
|
||||
|
|
@ -547,7 +547,7 @@ export function DesktopInstallOverlay({ enabled = true }: DesktopInstallOverlayP
|
|||
const currentElapsed = typeof currentStartedAt === 'number' ? formatElapsed(now - currentStartedAt) : ''
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-[1400] flex items-center justify-center bg-background/90 backdrop-blur-md p-4">
|
||||
<div className="fixed inset-0 z-(--z-setup) flex items-center justify-center bg-background/90 backdrop-blur-md p-4">
|
||||
<div className="flex w-full max-w-2xl max-h-[90vh] flex-col rounded-xl border border-(--stroke-nous) bg-card shadow-nous">
|
||||
{/* Header -- always visible, never scrolls */}
|
||||
<div className="flex flex-shrink-0 items-start gap-4 p-8 pb-4">
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ function RootErrorFallback({ error, reset }: ErrorBoundaryFallbackProps) {
|
|||
const { t } = useI18n()
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-[1500] grid place-items-center bg-(--ui-chat-surface-background) p-6">
|
||||
<div className="fixed inset-0 z-(--z-crash) grid place-items-center bg-(--ui-chat-surface-background) p-6">
|
||||
<ErrorState
|
||||
className="w-full max-w-[28rem]"
|
||||
description={error.message || t.errors.boundaryDesc}
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ export function FirstRunRemoteForm({ onBack }: FirstRunRemoteFormProps) {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-[1400] flex items-center justify-center bg-background/90 p-4 backdrop-blur-md">
|
||||
<div className="fixed inset-0 z-(--z-setup) flex items-center justify-center bg-background/90 p-4 backdrop-blur-md">
|
||||
<div className="flex w-full max-w-xl flex-col rounded-xl border border-(--stroke-nous) bg-card p-8 shadow-nous">
|
||||
<div className="flex items-start gap-4">
|
||||
<BrandMark className="size-11 shrink-0" />
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { BootFailureOverlay } from './boot-failure-overlay'
|
|||
import { GatewayConnectingOverlay } from './gateway-connecting-overlay'
|
||||
|
||||
// Repro for the "remote gateway → stuck on CONNECTING, no way to settings"
|
||||
// report. The connecting overlay (z-1200, full-screen, pointer-events on) used
|
||||
// report. The connecting overlay (full-screen, pointer-events on) used
|
||||
// to be shown whenever `gatewayState !== 'open' && !boot.error`. The ONLY escape
|
||||
// hatch — BootFailureOverlay, which has "Use local gateway" / "Sign in" /
|
||||
// "Retry" — only renders when `boot.error` is set.
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ export function GatewayConnectingOverlay() {
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'fixed inset-0 z-[1200] grid place-items-center bg-(--ui-chat-surface-background) transition-opacity duration-500 ease-out',
|
||||
'fixed inset-0 z-(--z-connecting) grid place-items-center bg-(--ui-chat-surface-background) transition-opacity duration-500 ease-out',
|
||||
overlayHidden ? 'pointer-events-none opacity-0' : 'opacity-100'
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -28,10 +28,10 @@ interface ModelPickerDialogProps {
|
|||
onSelect: (selection: { provider: string; model: string }) => void
|
||||
profile?: string
|
||||
/**
|
||||
* Optional class to apply to DialogContent. Use to override z-index when
|
||||
* stacking the picker on top of another fixed overlay (e.g. the desktop
|
||||
* onboarding overlay, which sits at z-1300; the default Dialog z-130 ends
|
||||
* up rendering underneath and blocks pointer events).
|
||||
* Optional class for DialogContent. Use it to lift the picker onto a higher
|
||||
* rung of the overlay ladder when it opens over another fixed overlay (the
|
||||
* desktop onboarding overlay, say) — on the default modal rung it renders
|
||||
* underneath and blocks pointer events.
|
||||
*/
|
||||
contentClassName?: string
|
||||
}
|
||||
|
|
@ -85,7 +85,7 @@ export function ModelPickerDialog({
|
|||
// Open the full onboarding provider selector to add/switch a provider.
|
||||
// Reuses the entire onboarding flow (OAuth rows, API-key form, device-code,
|
||||
// model-confirm) instead of duplicating provider UI here. Closes the picker
|
||||
// so the onboarding overlay (z-1300) isn't rendered underneath it.
|
||||
// so the onboarding overlay isn't rendered underneath it.
|
||||
const addProvider = () => {
|
||||
startManualOnboarding()
|
||||
onOpenChange(false)
|
||||
|
|
|
|||
|
|
@ -92,9 +92,9 @@ export function NotificationStack() {
|
|||
)
|
||||
}
|
||||
|
||||
// Portaled to <body> with a z above the Radix dialog layer (overlay z-[120],
|
||||
// content z-[130]) — see the top-center variant below for why.
|
||||
const REGION_BASE = 'pointer-events-none fixed z-[200] flex gap-2'
|
||||
// Portaled to <body> on the over-modal rung so a toast clears an open dialog —
|
||||
// see the top-center variant below for why.
|
||||
const REGION_BASE = 'pointer-events-none fixed z-(--z-over-modal) flex gap-2'
|
||||
|
||||
// Primary stack: top-center, collapsed to the latest toast with a "+N more"
|
||||
// expander + clear-all — the noisy/important surface (errors, warnings,
|
||||
|
|
|
|||
|
|
@ -308,15 +308,14 @@ function ConfirmingModelPanel({
|
|||
</div>
|
||||
|
||||
{/*
|
||||
ModelPickerDialog defaults to z-130 on its content, which renders
|
||||
UNDER the onboarding overlay (z-1300) and breaks pointer events.
|
||||
Bump it above with z-[1310] so the picker sits on top of the
|
||||
onboarding panel. The dialog's own dim-backdrop layer stays at
|
||||
its default z-120 — the onboarding overlay is already dimming
|
||||
the rest of the screen, so we don't want a second backdrop.
|
||||
ModelPickerDialog's content sits on the modal rung, which is below the
|
||||
onboarding overlay — it would render underneath and swallow pointer
|
||||
events. Lift it to the rung above onboarding. Its own dim-backdrop
|
||||
layer stays on the modal-backdrop rung: onboarding already dims the
|
||||
rest of the screen, so a second backdrop would double up.
|
||||
*/}
|
||||
<ModelPickerDialog
|
||||
contentClassName="z-[1310]"
|
||||
contentClassName="z-(--z-onboarding-popover)"
|
||||
currentModel={flow.currentModel}
|
||||
currentProvider={flow.providerSlug}
|
||||
onOpenChange={setPickerOpen}
|
||||
|
|
|
|||
|
|
@ -302,7 +302,7 @@ export function DesktopOnboardingOverlay({
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'fixed inset-0 z-1300 flex items-center justify-center bg-(--ui-chat-surface-background) p-6 transition-opacity duration-[520ms] ease-out',
|
||||
'fixed inset-0 z-(--z-onboarding) flex items-center justify-center bg-(--ui-chat-surface-background) p-6 transition-opacity duration-[520ms] ease-out',
|
||||
// On the bare confirm screen, hold the surface (text-out + hold) so the
|
||||
// per-element exit plays before it dissolves.
|
||||
bare && leaving ? '[transition-delay:660ms]' : '',
|
||||
|
|
|
|||
|
|
@ -45,10 +45,10 @@ export function SessionPickerDialog({ activeStoredSessionId, onOpenChange, onRes
|
|||
return (
|
||||
<DialogPrimitive.Root onOpenChange={onOpenChange} open={open}>
|
||||
<DialogPrimitive.Portal>
|
||||
<DialogPrimitive.Overlay className="fixed inset-0 z-[200] bg-black/15 backdrop-blur-[1px] data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0" />
|
||||
<DialogPrimitive.Overlay className="fixed inset-0 z-(--z-over-modal) bg-black/15 backdrop-blur-[1px] data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0" />
|
||||
<DialogPrimitive.Content
|
||||
aria-describedby={undefined}
|
||||
className="fixed left-1/2 top-[14vh] z-[210] w-[min(40rem,calc(100vw-2rem))] -translate-x-1/2 overflow-hidden rounded-xl border border-(--ui-stroke-secondary) bg-(--ui-chat-bubble-background) shadow-lg duration-150 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:slide-in-from-top-2 data-[state=open]:zoom-in-95"
|
||||
className="fixed left-1/2 top-[14vh] z-(--z-over-modal-content) w-[min(40rem,calc(100vw-2rem))] -translate-x-1/2 overflow-hidden rounded-xl border border-(--ui-stroke-secondary) bg-(--ui-chat-bubble-background) shadow-lg duration-150 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:slide-in-from-top-2 data-[state=open]:zoom-in-95"
|
||||
>
|
||||
<DialogPrimitive.Title className="sr-only">{t.commandCenter.sections.sessions}</DialogPrimitive.Title>
|
||||
<Command className="bg-transparent" loop>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ function DialogOverlay({ className, ...props }: React.ComponentProps<typeof Dial
|
|||
return (
|
||||
<DialogPrimitive.Overlay
|
||||
className={cn(
|
||||
'fixed inset-0 z-[120] pointer-events-auto bg-black/22 backdrop-blur-[0.125rem] data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0',
|
||||
'fixed inset-0 z-(--z-modal-backdrop) pointer-events-auto bg-black/22 backdrop-blur-[0.125rem] data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0',
|
||||
className
|
||||
)}
|
||||
data-slot="dialog-overlay"
|
||||
|
|
@ -130,7 +130,7 @@ function DialogContent({
|
|||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
className={cn(
|
||||
'fixed left-1/2 top-1/2 z-[130] pointer-events-auto flex max-h-[85vh] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-xl bg-(--ui-chat-bubble-background) text-[length:var(--conversation-text-font-size)] text-foreground shadow-nous duration-200 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
|
||||
'fixed left-1/2 top-1/2 z-(--z-modal) pointer-events-auto flex max-h-[85vh] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-xl bg-(--ui-chat-bubble-background) text-[length:var(--conversation-text-font-size)] text-foreground shadow-nous duration-200 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
|
||||
widthClass,
|
||||
className,
|
||||
// Callers often pass `gap-*` for the no-banner grid layout — suppress
|
||||
|
|
@ -174,7 +174,7 @@ function DialogContent({
|
|||
// Cap height at 85vh and let long content scroll inside the dialog
|
||||
// instead of overflowing off-screen (long cron titles, tool detail
|
||||
// dumps, etc.). Individual dialogs can still override via className.
|
||||
'fixed left-1/2 top-1/2 z-[130] pointer-events-auto grid max-h-[85vh] -translate-x-1/2 -translate-y-1/2 gap-3 overflow-y-auto rounded-xl border border-(--stroke-nous) bg-(--ui-chat-bubble-background) p-4 text-[length:var(--conversation-text-font-size)] text-foreground shadow-nous duration-200 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
|
||||
'fixed left-1/2 top-1/2 z-(--z-modal) pointer-events-auto grid max-h-[85vh] -translate-x-1/2 -translate-y-1/2 gap-3 overflow-y-auto rounded-xl border border-(--stroke-nous) bg-(--ui-chat-bubble-background) p-4 text-[length:var(--conversation-text-font-size)] text-foreground shadow-nous duration-200 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
|
||||
widthClass,
|
||||
className
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ function SelectContent({
|
|||
<SelectPrimitive.Portal container={container}>
|
||||
<SelectPrimitive.Content
|
||||
className={cn(
|
||||
'relative z-[140] max-h-72 min-w-32 overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-md 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=top]:slide-in-from-bottom-2 data-[side=right]:slide-in-from-left-2',
|
||||
'relative z-(--z-modal-popover) max-h-72 min-w-32 overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-md 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=top]:slide-in-from-bottom-2 data-[side=right]:slide-in-from-left-2',
|
||||
position === 'popper' &&
|
||||
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
||||
className
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ function TooltipContent({
|
|||
// rectangular dead space). Instant, no transition (delayDuration=0).
|
||||
// pointer-events-none: the tip must never steal hover/clicks from the
|
||||
// chrome underneath (titlebar tools, adjacent tabs, etc.).
|
||||
className={cn('pointer-events-none z-[200] w-fit max-w-64 select-none', className)}
|
||||
className={cn('pointer-events-none z-(--z-over-modal) w-fit max-w-64 select-none', className)}
|
||||
data-slot="tooltip-content"
|
||||
sideOffset={sideOffset}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -198,6 +198,28 @@
|
|||
--ui-diff-remove-border: var(--ui-red);
|
||||
--ui-diff-remove-background: color-mix(in srgb, var(--ui-red) 12%, transparent);
|
||||
--ui-diff-remove-foreground: color-mix(in srgb, var(--ui-red) 70%, #000);
|
||||
|
||||
/* Overlay ladder. DESIGN.md: app-wide surfaces must not compete through
|
||||
ad-hoc z-index literals — pick the rung that describes the surface.
|
||||
Values are deliberately sparse so a one-off can slot between two rungs
|
||||
without a renumber. Local stacking inside a component (a sticky header
|
||||
over its own scroll area) stays on plain `z-10`/`z-20`; these rungs are
|
||||
only for surfaces that float over the app. */
|
||||
--z-modal-backdrop: 120;
|
||||
--z-modal: 130;
|
||||
/* A select/dropdown/popover opened from inside a modal, portaled to body. */
|
||||
--z-modal-popover: 140;
|
||||
/* Must clear any open modal: toasts, tooltips, command-surface backdrops. */
|
||||
--z-over-modal: 200;
|
||||
--z-over-modal-content: 210;
|
||||
--z-switcher-backdrop: 219;
|
||||
--z-switcher: 220;
|
||||
/* Boot and blocking states, in the order they can stack. */
|
||||
--z-connecting: 1200;
|
||||
--z-onboarding: 1300;
|
||||
--z-onboarding-popover: 1310;
|
||||
--z-setup: 1400;
|
||||
--z-crash: 1500;
|
||||
--ui-bg-chrome: color-mix(
|
||||
in srgb,
|
||||
var(--theme-background-seed) var(--theme-mix-chrome),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue