headless-tooltip
v1.1.12
Published
Headless UI Component for building powerful React Tooltips!
Downloads
6,611
Maintainers
Readme
headless-tooltip
A lightweight, customizable tooltip component for React with zero styling opinions. Built with accessibility in mind.
If you find Headless-Tooltip useful, please consider giving it a ⭐
Table of Contents
- Features
- Installation
- Basic Usage
- API Reference
- Accessibility
- Advanced Usage
- Animation Example
- Contributing
Features
- 🎨 Truly headless: No predefined styles, full control over tooltip appearance
- ♿ Accessible: Follows WAI-ARIA Tooltip Pattern
- 🧩 Flexible: Supports custom content, including HTML and React components
- 📱 Responsive: Automatically adapts to different screen sizes
- 🔄 Interactive mode: Optional interactive tooltips that remain visible when hovering
- 🏹 Customizable arrow: Optional arrow that can be styled and positioned
- 🌐 Placement options: 12 different placement positions for tooltip
- ⌨️ Keyboard friendly: Fully keyboard accessible with proper focus management
- ✨ Animation ready: Built-in support for CSS transitions and animations
- 🎭 State-based styling: CSS data attributes for different tooltip states
Installation
npm
npm install headless-tooltipyarn
yarn add headless-tooltippnpm
pnpm add headless-tooltipBasic Usage
import { Tooltip } from 'headless-tooltip';
function Example() {
return (
<Tooltip content="This is a tooltip message">
<button>Hover me</button>
</Tooltip>
);
}Styled Example
import { Tooltip } from 'headless-tooltip';
function StyledExample() {
return (
<Tooltip
content={<span>This is a tooltip message</span>}
placement="bottom"
arrow={true}
className="max-w-80 rounded-lg bg-gray-900 px-3 py-2 text-xs font-normal text-white"
arrowClassName="bg-gray-900"
>
<button className="px-4 py-2 bg-blue-500 text-white rounded">
Hover me
</button>
</Tooltip>
);
}API Reference
Props
| Prop | Type | Default | Description |
| -------------------------- | ------------------------- | --------------- | ------------------------------------------------------------ |
| children | React.ReactNode | (required) | The element that triggers the tooltip |
| content | React.ReactNode | (required) | The content to be displayed in the tooltip |
| placement | Placement | 'top' | Tooltip placement relative to the trigger element |
| className | string | '' | Additional CSS classes to apply to the tooltip |
| offset | number | 4 | Distance between tooltip and trigger element in pixels |
| zIndex | number | undefined | Z-index value for the tooltip |
| open | boolean | undefined | Control tooltip visibility (makes it a controlled component) |
| openDelay | number | 300 | Delay in ms before showing the tooltip |
| closeDelay | number | 200 | Delay in ms before hiding the tooltip |
| disableInteractive | boolean | false | If true, tooltip will close when mouse leaves trigger |
| onOpenChange | (open: boolean) => void | undefined | Callback when tooltip visibility changes |
| portalContainer | HTMLElement | document.body | DOM element where tooltip portal will be rendered |
| arrow | boolean | false | Whether to show an arrow pointing to the trigger |
| arrowSize | number | 12 | Size of the arrow in pixels |
| arrowClassName | string | undefined | Additional CSS classes to apply to the arrow |
| transition | object | undefined | Configuration for tooltip enter/exit animations |
| transition.enable | boolean | false | Whether to enable transition animations |
| transition.enterDuration | number | 300 | Duration of the enter animation in milliseconds |
| transition.exitDuration | number | 300 | Duration of the exit animation in milliseconds |
Placement Types
The placement prop accepts the following values:
'top''right''bottom''left''top-start''top-end''right-start''right-end''bottom-start''bottom-end''left-start''left-end'
Accessibility
This tooltip implementation follows the WAI-ARIA Tooltip Pattern to ensure accessibility compliance:
- Uses appropriate ARIA attributes (
role="tooltip",aria-describedby) - Supports keyboard navigation with proper focus management
- Dismissible with Escape key
- Works with screen readers
- Triggered by both hover and focus events
Browser Support
The component is compatible with all modern browsers:
- Chrome (and Chromium-based browsers)
- Firefox
- Safari
- Edge
Advanced Usage
Controlled Mode
import { useState } from 'react';
import { Tooltip } from 'headless-tooltip';
function ControlledExample() {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<button onClick={() => setIsOpen(!isOpen)}>Toggle Tooltip</button>
<Tooltip
content="This is a controlled tooltip"
open={isOpen}
onOpenChange={setIsOpen}
>
<button>Hover me too</button>
</Tooltip>
</div>
);
}Interactive Tooltip
import { Tooltip } from 'headless-tooltip';
function InteractiveExample() {
return (
<Tooltip
content={
<div>
<p>Interactive tooltip with a button:</p>
<button onClick={() => alert('Clicked!')}>Click me</button>
</div>
}
disableInteractive={false}
>
<button>Hover for interactive tooltip</button>
</Tooltip>
);
}Animation Example
import { Tooltip } from 'headless-tooltip';
import './animations.css';
function ZoomTooltip() {
return (
<Tooltip
content="This tooltip zooms in and out!"
className="tooltip-base zoom-in-out"
transition={{
enable: true,
enterDuration: 400,
exitDuration: 400,
}}
>
<button>Hover for zoom animation</button>
</Tooltip>
);
}/* animations.css */
.tooltip-base {
background: #333;
color: white;
padding: 8px 12px;
border-radius: 6px;
font-size: 14px;
}
/* Zoom In/Out Animation */
.zoom-in-out[data-enter] {
opacity: 0;
}
.zoom-in-out[data-entering] {
opacity: 1;
animation: zoomIn 400ms ease-out;
}
.zoom-in-out[data-exiting] {
animation: zoomOut 400ms ease-in;
}
/* Keyframe Animations */
@keyframes zoomIn {
0% {
opacity: 0;
transform: scale(0.3);
}
50% {
opacity: 1;
}
100% {
opacity: 1;
transform: scale(1);
}
}
@keyframes zoomOut {
0% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 1;
transform: scale(0.3);
}
100% {
opacity: 0;
transform: scale(0.3);
}
}Contributing
Contributions are always welcome! Please feel free to submit a Pull Request.
