Files
biggus-dickus/web/src/components/ui/tooltip.tsx
T
logaritmisk 79a6567530 fix(web): dark-mode tokens for popup primitives + theme-color/color-scheme sync (#68)
Tooltip, toast, and combobox popups still hardcoded light colors
(bg-white, neutral-*, indigo-50) and rendered as white boxes in dark
mode; the objects-table page-size select did the same in app code.
Swap all of them to theme tokens (popover/accent/muted/destructive/
success) and replace the toast's literal "×" with the lucide X icon.

Wire browser chrome into the theme: color-scheme via CSS on
:root/.dark (follows the in-app toggle, not just the OS), a
theme-color meta kept in sync by the preload script and applyTheme(),
plus a unit test for the meta sync.

Extend check-no-raw-colors to also flag shadeless white/black
utilities outside components/ui/ so the objects-table case can't
recur.

Closes #68

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 09:42:57 +02:00

83 lines
2.4 KiB
TypeScript

import type { ReactElement, ReactNode } from "react";
import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip";
import { cn } from "@/lib/utils";
type Side = NonNullable<TooltipPrimitive.Positioner.Props["side"]>;
function TooltipProvider({ ...props }: TooltipPrimitive.Provider.Props) {
return <TooltipPrimitive.Provider {...props} />;
}
function TooltipRoot({ ...props }: TooltipPrimitive.Root.Props) {
return <TooltipPrimitive.Root data-slot="tooltip" {...props} />;
}
function TooltipTrigger({ ...props }: TooltipPrimitive.Trigger.Props) {
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
}
function TooltipPopup({ className, ...props }: TooltipPrimitive.Popup.Props) {
return (
<TooltipPrimitive.Popup
data-slot="tooltip-popup"
className={cn(
"rounded border bg-popover px-2 py-1 text-sm text-popover-foreground shadow-md",
className,
)}
{...props}
/>
);
}
function TooltipPositioner({ className, ...props }: TooltipPrimitive.Positioner.Props) {
return (
<TooltipPrimitive.Positioner
data-slot="tooltip-positioner"
className={cn("z-50", className)}
{...props}
/>
);
}
export type TooltipProps = {
/** Text shown in the tooltip popup. */
content: ReactNode;
/** The element the tooltip is attached to. Rendered as the trigger. */
children: ReactElement;
/** Which side of the trigger to place the popup. Defaults to `"right"`. */
side?: Side;
/** Pixel gap between the trigger and the popup. */
sideOffset?: number;
};
/**
* Standalone tooltip: wraps its own `Provider`, so it can be dropped in
* anywhere without an ancestor provider. `children` is delegated to the
* Base UI trigger via `render`, so the underlying element keeps its own
* tag/handlers (e.g. a `NavLink` for the collapsed sidebar).
*/
function Tooltip({ content, children, side = "right", sideOffset = 6 }: TooltipProps) {
return (
<TooltipProvider>
<TooltipRoot>
<TooltipTrigger render={children} />
<TooltipPrimitive.Portal>
<TooltipPositioner side={side} sideOffset={sideOffset}>
<TooltipPopup>{content}</TooltipPopup>
</TooltipPositioner>
</TooltipPrimitive.Portal>
</TooltipRoot>
</TooltipProvider>
);
}
export {
Tooltip,
TooltipProvider,
TooltipRoot,
TooltipTrigger,
TooltipPositioner,
TooltipPopup,
};