easy-tooltips
v3.0.3
Published
A lightweight, zero-dependency tooltip library using modern JavaScript and CSS.
Maintainers
Readme
easy-tooltips
A lightweight, zero-dependency tooltip library using modern JavaScript and CSS.
Just add data-easy-tooltip to any element! No setup or config required.
Features
- No dependencies
- Works with mouse, touch, and keyboard focus
- Customizable via CSS variables
- Automatically repositions and shifts to fit the screen
- Smooth, non-interrupting animations with smart skip-delay between adjacent tooltips
- Anchor to the trigger element, the cursor, or where the user first hovered
- Plain text or arbitrary HTML content
- Seamless body + arrow drawn as a single SVG path
- Compatible with Vue, React, Svelte, and more
Quick Start
Install via npm
npm install easy-tooltipsimport "easy-tooltips/styles.css"
import "easy-tooltips"Or use via CDN
https://www.jsdelivr.com/package/npm/easy-tooltips
Add tooltips to your HTML
<button data-easy-tooltip="Click to save your changes">Save</button>
<span data-easy-tooltip="This field is required">Username *</span>
<div data-easy-tooltip="Multi-line tooltips are supported too">Info</div>Multi-line text uses (newline). For full HTML content see Custom HTML below.
No additional setup is needed for Vue, React, Svelte, or other frameworks! Tooltips automatically update when the element updates!
Tooltips also show on keyboard focus. Natively focusable elements (buttons, links, inputs) work automatically; for other elements (such as a <div> or <span>), add tabindex="0" so they can receive focus.
Advanced Usage
Custom HTML
You can render custom HTML inside a tooltip using data-easy-tooltip-src. The value can be a CSS selector or the literal keyword next.
The matched element's content is copied into the tooltip.
Using a CSS selector
Point to any element in the document. The value is matched by id first, then as a CSS selector, so tip-shipping and #tip-shipping both work.
<button data-easy-tooltip-src="#tip-shipping">Shipping info</button>
<template id="tip-shipping">
<strong>Free shipping</strong> on orders over £50<br>
Delivered in 2 to 4 working days
</template>Using next
Use next to automatically pull content from the next DOM element.
The source element is automatically hidden.
<button data-easy-tooltip-src="next">Ingredients</button>
<div>
<ul>
<li>Oats</li>
<li>Honey</li>
<li>Sea salt</li>
</ul>
</div>Custom tooltip classes
For styling specific tooltips, add data-easy-tooltip-class. The value is applied as a class on the generated tooltip:
<button data-easy-tooltip="Saved!" data-easy-tooltip-class="success-tooltip">Save</button>
<button data-easy-tooltip="This cannot be undone!" data-easy-tooltip-class="danger-tooltip bold-tooltip">Delete</button>.success-tooltip {
--easy-tooltip-background-color: #f0fdf4;
--easy-tooltip-border-color: #27ae60;
--easy-tooltip-text-color: #27ae60;
}
.danger-tooltip {
--easy-tooltip-background-color: #fef2f2;
--easy-tooltip-border-color: #e74c3c;
--easy-tooltip-text-color: #e74c3c;
}
.bold-tooltip {
font-weight: bold;
}Preferred side
Use data-easy-tooltip-prefer to control which side a tooltip shows on. It still flips to the opposite side when there isn't room. Using left or right switches the tooltip to horizontal mode.
above(default): show above, fall back to belowbelow: show below, fall back to aboveleft: show to the left, fall back to the rightright: show to the right, fall back to the left
<button data-easy-tooltip="Shows below" data-easy-tooltip-prefer="below">Hover me</button>
<button data-easy-tooltip="Shows on the right" data-easy-tooltip-prefer="right">Hover me</button>Anchor modes
By default a tooltip is anchored to its trigger element. Use data-easy-tooltip-anchor to change the anchor point:
element(default): anchored to the trigger's bounding box.cursor: anchored to the cursor (or touch point), follows the cursor as it moves around the element. On touch, the tooltip appears at the tap point and tracks the finger if you drag.pin: anchored to the first cursor or touch point on entry, then frozen there. Continues tracking the page as you scroll so the anchor stays at the same content position.
<div data-easy-tooltip="I follow your cursor" data-easy-tooltip-anchor="cursor">…</div>
<div data-easy-tooltip="I stay where you first hovered" data-easy-tooltip-anchor="pin">…</div>Keyboard focus has no coordinates and automatically falls back to element anchoring.
Customization
You can style tooltips using CSS variables (recommended) or by targeting the tooltip classes directly. Note that some CSS variables are required for proper positioning:
:root {
/* Tooltip appearance */
--easy-tooltip-background-color: #fff; /* Background color */
--easy-tooltip-text-color: #000; /* Text color */
--easy-tooltip-border-color: #aaa; /* Border color */
--easy-tooltip-border-size: 1px; /* Border thickness */
--easy-tooltip-border-radius: 4px; /* Corner radius of the tooltip */
--easy-tooltip-padding: 8px 12px; /* Inner padding */
--easy-tooltip-max-width: 100%; /* Maximum tooltip width */
/* Positioning */
--easy-tooltip-distance: 16px; /* Distance from trigger element */
--easy-tooltip-viewport-padding: 16px; /* Minimum distance from screen edges */
--easy-tooltip-arrow-size: 16px; /* Arrow size (height defaults to width / 2; 0 to disable) */
--easy-tooltip-arrow-size: 16px 8px; /* Or specify arrow width and height separately */
--easy-tooltip-arrow-edge-buffer-x: 12px; /* Minimum arrow gap from where the border radius starts (for above or below tooltips) */
--easy-tooltip-arrow-edge-buffer-y: 6px; /* Minimum arrow gap from where the border radius starts (for left or right tooltips) */
--easy-tooltip-arrow-radius: 0; /* Border radius of the arrow tip */
/* Animation */
--easy-tooltip-animation-length: 0.15s; /* Duration of fade animation */
--easy-tooltip-animation-distance: 4px; /* Distance the tooltip slides in */
--easy-tooltip-delay: 0s; /* Base delay before the tooltip shows; always added */
--easy-tooltip-inactive-delay: 0.15s; /* Extra delay when no tooltip was recently active; drops to 0 once a tooltip is showing */
--easy-tooltip-cooldown: 0.15s; /* How long after the last tooltip closes before the inactive-delay applies again */
}Show delay and quick-switch
Easy-tooltips uses a two-part delay so that the first tooltip waits, but switching between adjacent tooltips feels instant:
- On the first hover, the tooltip waits
delay + inactive-delay(0 + 0.15sby default) before showing. This protects against accidental hovers. - Once a tooltip has fully appeared, all subsequent tooltips skip the inactive-delay and show in just
delay(0sby default, i.e. instantly). - When all tooltips have been closed for
cooldown(0.15sby default), behaviour resets to "first hover" again.
Each part is its own variable so you can tune them independently. For example, a slow, deliberate tooltip with a long initial wait but instant skip:
.slow-tooltip {
--easy-tooltip-inactive-delay: 1s;
}Or a permanent delay regardless of recent activity (e.g. a 500ms reveal on every tooltip):
.always-slow-tooltip {
--easy-tooltip-delay: 500ms;
}Advanced customization
The tooltip body and arrow are drawn as a single SVG path, so anything beyond the variables can be done by targeting the path directly with SVG-flavored CSS. .easy-tooltip-bg is the SVG element; .easy-tooltip-bg path is the path that draws the outline and fill.
Borders
The border is the SVG path's stroke, so any stroke property works:
/* Dashed border */
.dashed-tooltip .easy-tooltip-bg path {
stroke-dasharray: 5 3;
stroke-linecap: round;
}Backgrounds
fill on the SVG path only accepts a <paint> value, so CSS gradients (linear-gradient, etc.) won't apply directly. To use a gradient (or any other SVG paint server), declare it once in the document and reference it by id:
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
<defs>
<linearGradient id="brand-gradient" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#3498db"/>
<stop offset="100%" stop-color="#9b59b6"/>
</linearGradient>
</defs>
</svg>.gradient-tooltip .easy-tooltip-bg path {
fill: url(#brand-gradient);
}The same approach works for stroke (gradient borders), and for radial, conic, or pattern paint servers.
For a drop shadow that follows the full body + arrow shape, apply a filter to the SVG element:
.shadow-tooltip .easy-tooltip-bg {
filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.25));
}How it works
Easy-tooltips uses a smart positioning system that:
- Picks a side - Shows on the preferred side (above by default, or below/left/right) and flips to the opposite side when there isn't room
- Keeps it on screen - Shifts the tooltip along its edge (horizontally for above/below, vertically for left/right) so it stays within the viewport while the arrow keeps pointing at the element
- Falls back gracefully - When a tooltip can't fit on either side, it pins inside the viewport instead of overflowing
- Manages animations - Queues tooltip updates to prevent conflicts and flicker on rapid hover
- Skips the delay when grazing - The first hover waits a short delay to ignore accidental movement, but once any tooltip is showing, switching to adjacent tooltips is instant until you stop hovering for the cooldown period
- Stacks newest on top - When multiple tooltips are visible at once, the most recently activated one renders above the others
- Cleans up - Removes a tooltip automatically when its trigger element leaves the DOM
License
MIT © Ewan Howell
