Tooltip

PreviousNext

A composable tooltip component for displaying contextual information on hover or focus with full accessibility support.

DARK MODE
Click me
LIGHT MODE
Click me
POSITIONS
'use client';

import { useState } from 'react';
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@/ui/Tooltip';
import { Clickable } from '@/ui/Clickable';

export function TooltipDemo() {
  const [clickTooltipOpen, setClickTooltipOpen] = useState(false);

  return (
    <div className="flex flex-col gap-24 h-300">
      <TooltipProvider>
        DARK MODE
        <div className="flex gap-48 items-center">
          <Tooltip>
            <TooltipTrigger className="underline decoration-dashed cursor-help">
              Hover me
            </TooltipTrigger>
            <TooltipContent>This is a helpful tooltip</TooltipContent>
          </Tooltip>

          <Tooltip open={clickTooltipOpen}>
            <TooltipTrigger
              render={
                <Clickable
                  className="rounded-lg border-2 border-dashed border-gray-300 p-6 text-center hover:border-gray-400 hover:bg-gray-50"
                  onClick={() => setClickTooltipOpen(!clickTooltipOpen)}
                >
                  Click me
                </Clickable>
              }
            ></TooltipTrigger>
            <TooltipContent>This tooltip opens on click</TooltipContent>
          </Tooltip>

          {/* Always open */}
          <Tooltip open>
            <TooltipTrigger className="underline decoration-dashed cursor-help">
              Always open
            </TooltipTrigger>
            <TooltipContent>This tooltip is always open</TooltipContent>
          </Tooltip>
        </div>
        LIGHT MODE
        <div className="flex gap-48 items-center">
          <Tooltip>
            <TooltipTrigger className="underline decoration-dashed cursor-help">
              Hover me
            </TooltipTrigger>
            <TooltipContent theme="light">This is a helpful tooltip</TooltipContent>
          </Tooltip>

          <Tooltip open={clickTooltipOpen}>
            <TooltipTrigger
              render={
                <Clickable
                  className="rounded-lg border-2 border-dashed border-gray-300 p-6 text-center hover:border-gray-400 hover:bg-gray-50"
                  onClick={() => setClickTooltipOpen(!clickTooltipOpen)}
                >
                  Click me
                </Clickable>
              }
            ></TooltipTrigger>
            <TooltipContent theme="light">This tooltip opens on click</TooltipContent>
          </Tooltip>

          {/* Always open */}
          <Tooltip open>
            <TooltipTrigger className="underline decoration-dashed cursor-help">
              Always open
            </TooltipTrigger>
            <TooltipContent theme="light">This tooltip is always open</TooltipContent>
          </Tooltip>
        </div>
        POSITIONS
        <div className="flex gap-48 items-center">
          <Tooltip>
            <TooltipTrigger className="underline decoration-dashed cursor-help">
              Hover me
            </TooltipTrigger>
            <TooltipContent side="top">This tooltip is on the top</TooltipContent>
            <TooltipContent side="bottom">This tooltip is on the bottom</TooltipContent>
            <TooltipContent side="right">This tooltip is on the right</TooltipContent>
            <TooltipContent side="left">This tooltip is on the left</TooltipContent>
          </Tooltip>
        </div>
      </TooltipProvider>
    </div>
  );
}

Usage

import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@clubmed/trident-ui';
 
export function Component() {
  return (
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger className="underline decoration-dashed cursor-help">
          Hover me
        </TooltipTrigger>
        <TooltipContent>This is a helpful tooltip</TooltipContent>
      </Tooltip>
    </TooltipProvider>
  );
}

With Button

import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@clubmed/trident-ui';
import { Button } from '@clubmed/trident-ui';
 
export function Component() {
  return (
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger asChild>
          <Button variant="circle" icon="InfoCircle" size="small">
            <span className="sr-only">More information</span>
          </Button>
        </TooltipTrigger>
        <TooltipContent>Click for more details</TooltipContent>
      </Tooltip>
    </TooltipProvider>
  );
}

With Delay

import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@clubmed/trident-ui';
 
export function Component() {
  return (
    <TooltipProvider delay={600}>
      <Tooltip>
        <TooltipTrigger>Hover me</TooltipTrigger>
        <TooltipContent>Appears after 600ms</TooltipContent>
      </Tooltip>
    </TooltipProvider>
  );
}

Rich Content

import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@clubmed/trident-ui';
 
export function Component() {
  return (
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger>Keyboard shortcut</TooltipTrigger>
        <TooltipContent>
          <div className="flex items-center gap-8">
            <span>Save document</span>
            <kbd
              data-slot="kbd"
              className="bg-background text-foreground px-6 py-2 rounded text-xs font-mono"
            >
              ⌘S
            </kbd>
          </div>
        </TooltipContent>
      </Tooltip>
    </TooltipProvider>
  );
}

In Forms

import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@clubmed/trident-ui';
 
export function Component() {
  return (
    <TooltipProvider>
      <form>
        <div className="flex items-center gap-8">
          <label htmlFor="username">Username</label>
          <Tooltip>
            <TooltipTrigger>ⓘ</TooltipTrigger>
            <TooltipContent>Username must be 3-20 characters</TooltipContent>
          </Tooltip>
        </div>
        <input id="username" type="text" />
      </form>
    </TooltipProvider>
  );
}

Light Theme

import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@clubmed/trident-ui';
 
export function Component() {
  return (
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger className="underline decoration-dashed cursor-help">
          Hover me
        </TooltipTrigger>
        <TooltipContent theme="light">Light themed tooltip</TooltipContent>
      </Tooltip>
    </TooltipProvider>
  );
}

API Reference

TooltipProvider

Wraps tooltip instances and provides shared configuration.

PropTypeDefaultDescription
delaynumber0Delay in milliseconds before tooltip appears
childrenReactNodeundefinedTooltip components

Tooltip

The root tooltip component that manages state.

PropTypeDefaultDescription
openbooleanundefinedControlled open state
defaultOpenbooleanfalseInitial open state
onOpenChange(open: boolean) => voidundefinedCallback when open state changes
childrenReactNodeundefinedTrigger and content components

TooltipTrigger

The element that triggers the tooltip display.

PropTypeDefaultDescription
asChildbooleanfalseMerge props onto child instead of wrapping
childrenReactNodeundefinedTrigger element
...propsbutton props-Standard button attributes for the trigger

TooltipContent

The popup content with positioning and styling.

PropTypeDefaultDescription
side'top' | 'right' | 'bottom' | 'left''top'Placement side relative to trigger
sideOffsetnumber4Distance from trigger in pixels
align'start' | 'center' | 'end''center'Alignment relative to trigger
alignOffsetnumber0Offset along the alignment axis (px)
theme'dark' | 'light''dark'Color theme (dark or light)
classNamestringundefinedAdditional CSS classes
childrenReactNodeundefinedTooltip content

Positioning

The tooltip can be positioned on any side of the trigger with various alignment options:

// Position on different sides
<TooltipContent side="top">Top</TooltipContent>
<TooltipContent side="right">Right</TooltipContent>
<TooltipContent side="bottom">Bottom</TooltipContent>
<TooltipContent side="left">Left</TooltipContent>
 
// Align relative to trigger
<TooltipContent align="start">Start aligned</TooltipContent>
<TooltipContent align="center">Center aligned</TooltipContent>
<TooltipContent align="end">End aligned</TooltipContent>
 
// Custom offsets
<TooltipContent sideOffset={20} alignOffset={10}>
  Custom offset
</TooltipContent>

Styling

The tooltip comes with default styling using Tailwind classes. Customize by adding your own classes:

<TooltipContent className="bg-turquoise text-white">
  Custom colored tooltip
</TooltipContent>

Default Styles

Dark theme (default):

  • Background: bg-black
  • Text: text-white

Light theme:

  • Background: bg-lightGrey
  • Text: text-black

Shared styles:

  • Border radius: rounded-8
  • Padding: p-8
  • Max width: max-w-xs
  • Font size: text-xs
  • Z-index: z-50
  • Includes animated arrow pointer that matches the theme

Accessibility

The Tooltip component follows WAI-ARIA guidelines:

  • ✅ Keyboard navigation support (focus/blur triggers)
  • ✅ Screen reader support
  • ✅ Proper ARIA attributes
  • ✅ Automatic ID management

Keyboard Interactions

  • Tab - Focus trigger element, showing tooltip
  • Shift+Tab - Move focus away, hiding tooltip

Best Practices

  1. Keep content concise - Tooltips should be brief (under 50 characters when possible)
  2. Don't hide critical information - Tooltips are supplementary; critical info should be visible
  3. Make triggers keyboard accessible - Use focusable elements (buttons, links, etc.)
  4. Avoid interactive content - Don't put buttons or links in tooltips (use Popover instead)
  5. Provide appropriate delays - 200-600ms is recommended for better UX
// Good: Accessible trigger
<TooltipTrigger asChild>
  <button aria-label="More information">
    <Icon name="InfoCircle" />
  </button>
</TooltipTrigger>
 
// Bad: Non-focusable trigger
<TooltipTrigger>
  <div>Hover me</div>
</TooltipTrigger>

Notes

  • The tooltip uses Base UI's @base-ui/react/tooltip primitives for reliable positioning and accessibility
  • Tooltips are rendered in a portal to prevent stacking context issues
  • Animations are CSS-based for optimal performance
  • Multiple tooltips can share the same TooltipProvider for consistent delay settings
  • The arrow automatically adjusts position based on the tooltip's placement