diff --git a/src/components/ui/overlayPresence.test.tsx b/src/components/ui/overlayPresence.test.tsx
new file mode 100644
index 00000000..53886710
--- /dev/null
+++ b/src/components/ui/overlayPresence.test.tsx
@@ -0,0 +1,60 @@
+import { render, screen } from '@testing-library/react'
+import { describe, expect, it } from 'vitest'
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from './tooltip'
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from './popover'
+
+const PRESENCE_ANIMATION_CLASS_PARTS = [
+ 'animate-',
+ 'fade-',
+ 'zoom-',
+ 'slide-in-from',
+]
+
+function expectNoPresenceAnimationClasses(element: HTMLElement) {
+ const unstableClasses = element.className
+ .split(/\s+/)
+ .filter((className) =>
+ PRESENCE_ANIMATION_CLASS_PARTS.some((part) => className.includes(part)),
+ )
+
+ expect(unstableClasses).toEqual([])
+}
+
+describe('overlay presence stability', () => {
+ it('keeps tooltip content free of Radix presence animation classes', () => {
+ render(
+
+
+
+
+
+ Tooltip copy
+
+ ,
+ )
+
+ expectNoPresenceAnimationClasses(screen.getByTestId('tooltip-content'))
+ })
+
+ it('keeps popover content free of Radix presence animation classes', () => {
+ render(
+
+
+
+
+ Popover copy
+ ,
+ )
+
+ expectNoPresenceAnimationClasses(screen.getByTestId('popover-content'))
+ })
+})
diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx
index c618fe7e..c98c434b 100644
--- a/src/components/ui/popover.tsx
+++ b/src/components/ui/popover.tsx
@@ -30,7 +30,7 @@ function PopoverContent({
sideOffset={sideOffset}
collisionPadding={collisionPadding}
className={cn(
- "bg-popover text-popover-foreground 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=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-[12000] w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
+ "bg-popover text-popover-foreground z-[12000] w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
className
)}
{...props}
@@ -45,39 +45,48 @@ function PopoverAnchor({
return
}
-function PopoverHeader({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- )
+interface PopoverTextSlotProps extends React.HTMLAttributes {
+ baseClassName: string
+ element: "div" | "p"
+ slot: string
}
-function PopoverTitle({ className, ...props }: React.ComponentProps<"h2">) {
- return (
-
- )
-}
-
-function PopoverDescription({
+function PopoverTextSlot({
+ baseClassName,
className,
+ element,
+ slot,
...props
-}: React.ComponentProps<"p">) {
+}: PopoverTextSlotProps) {
+ return React.createElement(
+ element,
+ {
+ "data-slot": slot,
+ className: cn(baseClassName, className),
+ ...props,
+ },
+ )
+}
+
+function PopoverHeader(props: React.ComponentProps<"div">) {
return (
-
)
}
+function PopoverTitle(props: React.ComponentProps<"div">) {
+ return
+}
+
+function PopoverDescription(props: React.ComponentProps<"p">) {
+ return
+}
+
export {
Popover,
PopoverTrigger,
diff --git a/src/components/ui/tooltip.tsx b/src/components/ui/tooltip.tsx
index d80a1446..62345776 100644
--- a/src/components/ui/tooltip.tsx
+++ b/src/components/ui/tooltip.tsx
@@ -42,7 +42,7 @@ function TooltipContent({
data-slot="tooltip-content"
sideOffset={sideOffset}
className={cn(
- "bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
+ "bg-foreground text-background z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
className
)}
{...props}