From bf785e8df076dbd553c3336f586fbc6a5eee2978 Mon Sep 17 00:00:00 2001 From: yyh Date: Mon, 2 Mar 2026 22:30:46 +0800 Subject: [PATCH] fix(web): unify overlay z-index, decouple Placement type, and improve animation a11y - Add z-50 to all overlay Positioners so overlays inside a Dialog (e.g. Tooltip on a dialog button) are not clipped by its backdrop - Replace @floating-ui/react Placement import with self-owned type definition to remove the transitive type dependency - Change Dialog popup transition-all to explicit transition-[transform,scale,opacity] to avoid animating unintended CSS properties - Add motion-reduce:transition-none to all animated overlay elements for prefers-reduced-motion compliance --- web/app/components/base/ui/dialog/index.tsx | 10 +++--- .../base/ui/dropdown-menu/index.tsx | 6 ++-- web/app/components/base/ui/placement.ts | 31 +++++++++++++------ web/app/components/base/ui/popover/index.tsx | 6 ++-- web/app/components/base/ui/select/index.tsx | 6 ++-- web/app/components/base/ui/tooltip/index.tsx | 4 +-- 6 files changed, 38 insertions(+), 25 deletions(-) diff --git a/web/app/components/base/ui/dialog/index.tsx b/web/app/components/base/ui/dialog/index.tsx index 61b9309138..b4b5f728b6 100644 --- a/web/app/components/base/ui/dialog/index.tsx +++ b/web/app/components/base/ui/dialog/index.tsx @@ -1,8 +1,10 @@ 'use client' // z-index strategy (relies on root `isolation: isolate` in layout.tsx): -// Tooltip / Popover / Dropdown — no z-index, DOM order is sufficient -// Dialog backdrop + popup — z-50, ensures modal covers non-modal portals +// All overlay primitives (Tooltip / Popover / Dropdown / Select / Dialog) — z-50 +// Overlays share the same z-index; DOM order handles stacking when multiple are open. +// This ensures overlays inside a Dialog (e.g. a Tooltip on a dialog button) render +// above the dialog backdrop instead of being clipped by it. // Toast — z-[99], always on top (defined in toast component) import { Dialog as BaseDialog } from '@base-ui/react/dialog' @@ -31,14 +33,14 @@ export function DialogContent({ diff --git a/web/app/components/base/ui/dropdown-menu/index.tsx b/web/app/components/base/ui/dropdown-menu/index.tsx index 85a81968d4..3eac03f7db 100644 --- a/web/app/components/base/ui/dropdown-menu/index.tsx +++ b/web/app/components/base/ui/dropdown-menu/index.tsx @@ -1,6 +1,6 @@ 'use client' -import type { Placement } from '@floating-ui/react' +import type { Placement } from '@/app/components/base/ui/placement' import { Menu } from '@base-ui/react/menu' import * as React from 'react' import { parsePlacement } from '@/app/components/base/ui/placement' @@ -64,13 +64,13 @@ function renderDropdownMenuPopup({ align={align} sideOffset={sideOffset} alignOffset={alignOffset} - className={cn('outline-none', className)} + className={cn('z-50 outline-none', className)} {...positionerProps} >