Skip to content

Tooltip

Display contextual information on hover or focus. Perfect for providing hints, descriptions, and additional context without cluttering the interface. Built for Pulse Framework with full reactivity support.

Import

tsx
import { Tooltip, Pulse } from '@odyssee/components';

Basic Usage

Code Éditable
Résultat

Placements

Control tooltip positioning with the placement prop.

Code Éditable
Résultat

Auto Positioning

Let the tooltip automatically find the best position.

Code Éditable
Résultat

Variants

The Tooltip component supports two variants: dark and light.

Code Éditable
Résultat

With Icons

Add tooltips to icon buttons for better UX.

Code Éditable
Résultat

Click Trigger

Show tooltip on click instead of hover.

tsx
const clickTooltip = (
  <Tooltip content="Click triggered tooltip" trigger="click">
    <Button>Click me</Button>
  </Tooltip>
);

With Delays

Control show and hide delays for better UX.

tsx
const delayedTooltip = (
  <Tooltip
    content="This tooltip has a delay"
    showDelay={500}
    hideDelay={200}
  >
    <Button>Hover me</Button>
  </Tooltip>
);

On Form Elements

Add helpful hints to form inputs.

tsx
const FormWithTooltips = () => (
  <form class="space-y-4">
    <div>
      <label class="flex items-center gap-2">
        Username
        <Tooltip content="Must be 3-20 characters, alphanumeric only">
          <span class="text-gray-400 cursor-help">ℹ️</span>
        </Tooltip>
      </label>
      <Input placeholder="Enter username" />
    </div>

    <div>
      <label class="flex items-center gap-2">
        Password
        <Tooltip content="Minimum 8 characters, include uppercase, lowercase, and numbers">
          <span class="text-gray-400 cursor-help">ℹ️</span>
        </Tooltip>
      </label>
      <Input type="password" placeholder="Enter password" />
    </div>
  </form>
);

On Disabled Elements

Show why an element is disabled.

tsx
const disabledWithTooltip = (
  <Tooltip content="Please complete the form first">
    <span class="inline-block">
      <Button disabled={true}>
        Submit
      </Button>
    </span>
  </Tooltip>
);

Rich Content

Use formatted text in tooltips.

tsx
const richTooltip = (
  <Tooltip
    content={
      <div class="space-y-1">
        <div class="font-semibold">Keyboard Shortcuts</div>
        <div class="text-xs">
          <div>⌘ + K - Search</div>
          <div>⌘ + S - Save</div>
          <div>⌘ + P - Print</div>
        </div>
      </div>
    }
  >
    <Button icon="⌨️">Shortcuts</Button>
  </Tooltip>
);

With Arrow

Add an arrow pointer to the tooltip.

tsx
const arrowTooltip = (
  <Tooltip
    content="Tooltip with arrow"
    arrow={true}
  >
    <Button>Hover me</Button>
  </Tooltip>
);

In Navigation

Add tooltips to navigation items.

tsx
const NavigationWithTooltips = () => (
  <nav class="flex gap-1">
    <Tooltip content="Dashboard" placement="bottom">
      <a href="/dashboard" class="p-2 rounded hover:bg-gray-100">
        🏠
      </a>
    </Tooltip>
    <Tooltip content="Messages" placement="bottom">
      <a href="/messages" class="p-2 rounded hover:bg-gray-100">
        💬
      </a>
    </Tooltip>
    <Tooltip content="Notifications" placement="bottom">
      <a href="/notifications" class="p-2 rounded hover:bg-gray-100">
        🔔
      </a>
    </Tooltip>
    <Tooltip content="Settings" placement="bottom">
      <a href="/settings" class="p-2 rounded hover:bg-gray-100">
        ⚙️
      </a>
    </Tooltip>
  </nav>
);

Complete Example

Here's a comprehensive toolbar with tooltips:

tsx
import { Tooltip, Button, Badge, Pulse } from '@odyssee/components';

const EditorToolbar = () => {
  const isBold = Pulse.signal(false);
  const isItalic = Pulse.signal(false);
  const isUnderline = Pulse.signal(false);

  const toggleFormat = (format: string) => {
    switch(format) {
      case 'bold':
        isBold(!isBold());
        break;
      case 'italic':
        isItalic(!isItalic());
        break;
      case 'underline':
        isUnderline(!isUnderline());
        break;
    }
  };

  return (
    <div class="border border-gray-200 rounded-lg p-2 dark:border-gray-700">
      <div class="flex items-center gap-1">
        {/* Text formatting */}
        <Tooltip content="Bold (⌘+B)" placement="bottom">
          <Button
            variant={isBold() ? 'solid' : 'ghost'}
            size="sm"
            onClick={() => toggleFormat('bold')}
          >
            <strong>B</strong>
          </Button>
        </Tooltip>

        <Tooltip content="Italic (⌘+I)" placement="bottom">
          <Button
            variant={isItalic() ? 'solid' : 'ghost'}
            size="sm"
            onClick={() => toggleFormat('italic')}
          >
            <em>I</em>
          </Button>
        </Tooltip>

        <Tooltip content="Underline (⌘+U)" placement="bottom">
          <Button
            variant={isUnderline() ? 'solid' : 'ghost'}
            size="sm"
            onClick={() => toggleFormat('underline')}
          >
            <u>U</u>
          </Button>
        </Tooltip>

        <div class="w-px h-6 bg-gray-300 mx-1" />

        {/* Alignment */}
        <Tooltip content="Align Left" placement="bottom">
          <Button variant="ghost" size="sm">

          </Button>
        </Tooltip>

        <Tooltip content="Align Center" placement="bottom">
          <Button variant="ghost" size="sm">

          </Button>
        </Tooltip>

        <Tooltip content="Align Right" placement="bottom">
          <Button variant="ghost" size="sm">

          </Button>
        </Tooltip>

        <div class="w-px h-6 bg-gray-300 mx-1" />

        {/* Lists */}
        <Tooltip content="Bullet List" placement="bottom">
          <Button variant="ghost" size="sm">

          </Button>
        </Tooltip>

        <Tooltip content="Numbered List" placement="bottom">
          <Button variant="ghost" size="sm">

          </Button>
        </Tooltip>

        <div class="w-px h-6 bg-gray-300 mx-1" />

        {/* Insert */}
        <Tooltip content="Insert Link (⌘+K)" placement="bottom">
          <Button variant="ghost" size="sm">
            🔗
          </Button>
        </Tooltip>

        <Tooltip content="Insert Image" placement="bottom">
          <Button variant="ghost" size="sm">
            🖼️
          </Button>
        </Tooltip>

        <Tooltip content="Insert Code Block" placement="bottom">
          <Button variant="ghost" size="sm">
            💻
          </Button>
        </Tooltip>

        <div class="flex-1" />

        {/* Actions */}
        <Tooltip content="Help & Documentation" placement="bottom">
          <Button variant="ghost" size="sm" icon="❓" />
        </Tooltip>

        <Tooltip content="Auto-save enabled">
          <Badge variant="soft" color="success" size="sm">
            ✓ Saved
          </Badge>
        </Tooltip>
      </div>
    </div>
  );
};

Props

PropTypeDefaultDescription
contentstring | HTMLElementRequiredTooltip content
childrenHTMLElementRequiredElement to attach tooltip to
placement"top" | "right" | "bottom" | "left" | "auto""top"Tooltip position
trigger"hover" | "click" | "focus""hover"Trigger event
variant"dark" | "light""dark"Visual style
arrowbooleanfalseShow arrow pointer
showDelaynumber0Delay before showing (ms)
hideDelaynumber0Delay before hiding (ms)
classNamestring-Additional CSS classes
idstring-HTML id attribute
stylestring | CSSStyleDeclaration-Inline styles

Accessibility

The Tooltip component follows accessibility best practices:

  • ✅ Uses proper role="tooltip" attribute
  • ✅ Keyboard accessible (focus trigger)
  • ✅ Screen reader friendly
  • ✅ Respects prefers-reduced-motion
  • ✅ Dismissible with Escape key
  • ✅ Proper ARIA attributes

ARIA Attributes

tsx
const accessibleTooltip = (
  <Tooltip
    content="This is an accessible tooltip"
    aria-label="Additional information"
  >
    <Button aria-describedby="tooltip-id">
      More Info
    </Button>
  </Tooltip>
);

Best Practices

✅ Do

  • Keep tooltip text concise and helpful
  • Use for supplementary information
  • Show keyboard shortcuts
  • Explain icon meanings
  • Provide context for disabled elements
  • Use appropriate placements
tsx
// Good: Concise and helpful
const goodTooltip = (
  <Tooltip content="Save changes (⌘+S)">
    <Button icon="💾">Save</Button>
  </Tooltip>
);

❌ Don't

  • Don't use for critical information (not always visible)
  • Don't use very long text (consider modal instead)
  • Don't hide essential UI elements
  • Don't nest tooltips
  • Don't use for mobile-only interfaces (no hover)
  • Don't use for form validation errors (use inline messages)
tsx
// Bad: Critical info in tooltip
const badTooltip = (
  <Tooltip content="IMPORTANT: This will delete all your data permanently and cannot be undone. Make sure you have a backup before proceeding.">
    <Button color="danger">Delete</Button>
  </Tooltip>
);

// Better: Use modal for important warnings
const betterApproach = (
  <Button 
    color="danger" 
    onClick={() => showConfirmModal()}
  >
    Delete
  </Button>
);

Use Cases

Icon Buttons

tsx
const iconButtons = (
  <div class="flex gap-2">
    <Tooltip content="Copy to clipboard">
      <Button variant="ghost" icon="📋" onClick={copyToClipboard} />
    </Tooltip>
    <Tooltip content="Download file">
      <Button variant="ghost" icon="⬇️" onClick={downloadFile} />
    </Tooltip>
  </div>
);

Table Actions

tsx
const tableRow = (
  <tr>
    <td>John Doe</td>
    <td>john@example.com</td>
    <td class="flex gap-1">
      <Tooltip content="View details">
        <Button variant="ghost" size="sm" icon="👁️" />
      </Tooltip>
      <Tooltip content="Edit user">
        <Button variant="ghost" size="sm" icon="✏️" />
      </Tooltip>
      <Tooltip content="Delete user">
        <Button variant="ghost" size="sm" icon="🗑️" color="danger" />
      </Tooltip>
    </td>
  </tr>
);

Status Indicators

tsx
const statusBadge = (
  <Tooltip content="Last updated: 2 minutes ago">
    <Badge color="success" dot={true}>
      Active
    </Badge>
  </Tooltip>
);

Truncated Text

tsx
const truncatedText = (
  <Tooltip content="This is the full text that doesn't fit in the available space">
    <div class="max-w-xs truncate">
      This is the full text that doesn't fit...
    </div>
  </Tooltip>
);

Mobile Considerations

Tooltips rely on hover, which doesn't work well on mobile. Consider alternatives:

tsx
const MobileAwareTooltip = ({ content, children }) => {
  const isMobile = Pulse.signal(window.innerWidth < 768);

  // Listen for resize
  window.addEventListener('resize', () => {
    isMobile(window.innerWidth < 768);
  });

  if (isMobile()) {
    // On mobile, use click or show info icon
    return (
      <div class="flex items-center gap-2">
        {children}
        <Button 
          variant="ghost" 
          size="sm" 
          icon="ℹ️"
          onClick={() => alert(content)}
        />
      </div>
    );
  }

  // On desktop, use normal tooltip
  return (
    <Tooltip content={content}>
      {children}
    </Tooltip>
  );
};

Styling & Theming

All tooltip styles use Tailwind CSS classes and support dark mode automatically.

Custom Styling

tsx
const customTooltip = (
  <Tooltip
    content="Custom styled tooltip"
    className="bg-purple-600 text-white font-bold"
  >
    <Button>Hover me</Button>
  </Tooltip>
);

TypeScript

Full TypeScript support with complete type definitions:

tsx
import type { TooltipProps } from '@odyssee/components';

const props: TooltipProps = {
  content: 'TypeScript tooltip',
  placement: 'top',
  trigger: 'hover',
  variant: 'dark',
  showDelay: 100
};

const tooltip = (
  <Tooltip {...props}>
    <Button>Hover</Button>
  </Tooltip>
);
  • Popover - For interactive content
  • Modal - For complex information
  • Badge - For inline status indicators

Version: 1.0.0
Last Updated: January 2025

Released under the MIT License.