All components
Tooltip
Short contextual labels with a default panel style and an inverted variant.
123456789101112131415161718192021222324import { Tooltip } from "#components/ui/ark-ui/tooltip.tsx";import { Button } from "#components/ui/ark-ui/button.tsx";
export function Example() { return ( // variant="inverted" flips the surface; the positioner portals to // <body> and the machine handles hover intent and delays. <Tooltip.Root variant="inverted" openDelay={200} closeDelay={100}> <Tooltip.Trigger asChild> <Button variant="surface">Copy</Button> </Tooltip.Trigger> <Tooltip.Portal> <Tooltip.Positioner> <Tooltip.Content> <Tooltip.Arrow> <Tooltip.ArrowTip /> </Tooltip.Arrow> Copy to clipboard </Tooltip.Content> </Tooltip.Positioner> </Tooltip.Portal> </Tooltip.Root> );}Variants
inverted
Install
1npx stylexblocks add tooltipSource
components/ui/ark-ui/tooltip.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114import * as stylex from "@stylexjs/stylex";import { Tooltip as ArkTooltip } from "@ark-ui/react/tooltip";import { Portal } from "@ark-ui/react/portal";import { createSlotRecipeContext } from "#lib/ark-ui/create-slot-recipe-context.tsx";import { defineSlotRecipe, fromMap } from "#lib/recipe-props.ts";import { durations } from '@stylexblocks/ui/tokens/durations.stylex.ts'import { fontSizes } from '@stylexblocks/ui/tokens/font-sizes.stylex.ts'import { fontWeights } from '@stylexblocks/ui/tokens/font-weights.stylex.ts'import { lineHeights } from '@stylexblocks/ui/tokens/line-heights.stylex.ts'import { semanticColors } from '@stylexblocks/ui/semantic-tokens/colors.stylex.ts'import { semanticRadii } from '@stylexblocks/ui/semantic-tokens/radii.stylex.ts'import { semanticShadows } from '@stylexblocks/ui/semantic-tokens/shadows.stylex.ts'import { sizes } from '@stylexblocks/ui/tokens/sizes.stylex.ts'import { spacing } from '@stylexblocks/ui/tokens/spacing.stylex.ts'import { zIndices } from '@stylexblocks/ui/tokens/z-indices.stylex.ts'
const tooltipContent = stylex.create({ base: { '--tooltip-bg': semanticColors.bg, display: 'flex', alignItems: 'center', backgroundColor: 'var(--tooltip-bg)', color: semanticColors.fg, paddingInline: spacing._2_5, paddingBlock: spacing._1, borderRadius: semanticRadii.panel, fontWeight: fontWeights.medium, fontSize: fontSizes.xs, lineHeight: lineHeights.xs, boxShadow: semanticShadows.overlay, maxWidth: sizes.xs, zIndex: zIndices.tooltip, transformOrigin: 'var(--transform-origin)', animationName: { default: null, ':is([data-open], [data-state="open"])': 'scale-in, fade-in', ':is([data-closed], [data-state="closed"])': 'scale-out, fade-out', }, animationDuration: { default: null, ':is([data-open], [data-state="open"])': durations.fast, ':is([data-closed], [data-state="closed"])': durations.fast, }, },})
const tooltipContentVariants = stylex.create({ inverted: { '--tooltip-bg': semanticColors.bgInverted, color: semanticColors.fgInverted, },})
const tooltipArrow = stylex.create({ base: { '--arrow-size': sizes._2, '--arrow-background': 'var(--tooltip-bg)', zIndex: -1, },})
const tooltipArrowTip = stylex.create({ base: { borderTopWidth: '1px', borderInlineStartWidth: '1px', borderColor: 'var(--tooltip-bg)', },})
export type TooltipVariant = keyof typeof tooltipContentVariants
// The generated variant map is sparse (`inverted` only). An empty `solid`// entry is the implicit default panel look so `defaultVariants` type-checks.const tooltipPlain = stylex.create({ solid: {} });
const tooltipRecipe = defineSlotRecipe({ slots: { content: { styles: tooltipContent, variants: { solid: tooltipPlain.solid, inverted: tooltipContentVariants.inverted, }, }, arrow: { styles: tooltipArrow }, arrowTip: { styles: tooltipArrowTip }, }, defaultVariants: { variant: "solid" },});
const { withRootProvider, withContext } = createSlotRecipeContext(tooltipRecipe);
/** * Tooltip — Ark's tooltip machine wearing the tooltip slot recipe: real * portal to `document.body`, hover/focus intent with open and close delays, * and anchor positioning all come from the machine. `<Tooltip.Root variant>` * resolves the recipe once; parts inherit it through context and compose via * `asChild`. Portal and Positioner stay unstyled engine parts. */export const Tooltip = { Root: withRootProvider(ArkTooltip.Root, "TooltipRoot"), Trigger: ArkTooltip.Trigger, Context: ArkTooltip.Context, Portal, Positioner: ArkTooltip.Positioner, Content: withContext( ArkTooltip.Content, (v) => [tooltipContent.base, fromMap(tooltipContentVariants, v.variant)], "TooltipContent", ), Arrow: withContext(ArkTooltip.Arrow, "arrow", "TooltipArrow"), ArrowTip: withContext(ArkTooltip.ArrowTip, "arrowTip", "TooltipArrowTip"),};