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 or frameworks required
  • Works with mouse, touch, and keyboard focus
  • Customizable via CSS variables
  • Gradient and image backgrounds and borders
  • Smart positioning and viewport detection
  • Smooth 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
  • Works in Vue, React, Svelte, and more

Demos

Tooltips in little UI scenes. The first is the default look; the rest combine variables with a little custom CSS. Hover or focus the elements in each scene.

PRO
$12/mo
  • Unlimited projects i
  • Priority support i
  • Advanced analytics i
Upgrade
A
Ava Chen
2h ago
Just shipped the new release! 🚀
👍 ❤️ 😂 🎉
Danger Zone
Delete account Delete
Reset settings Reset
Sign out everywhere Sign out
Never Gonna Give You Up
Rick Astley
Frontend
HTML
CSS
JavaScript
Backend
Node
Database
API
Active Users
24,891
+12.4%
Conversion
3.8%
−0.3%
Revenue
$48.2k
+5.1%
Avg. Session
4m 12s
+0.6%
TromsøKP 6
ReykjavíkKP 4
FairbanksKP 5
Preikestolen overlooking Lysefjord

Basic Usage

Simply add the data-easy-tooltip attribute to any element:

Button

Text Element

Hover over username

Icon

i
<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&#10;are supported">Info</div>

Custom HTML

Use data-easy-tooltip-src to pull content from another element:

CSS Selector

From Next Element

  • Oats
  • Honey
  • Sea salt
<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-4 working days
</template>

<button class="btn btn-success" data-easy-tooltip-src="next">Ingredients</button>
<div style="display: none;">
  <ul><li>Oats</li><li>Honey</li><li>Sea salt</li></ul>
</div>

Dynamic Tooltips

Tooltips automatically update when the data-easy-tooltip attribute changes. Hover over the elements below to see live updates:

Live Counter

Current Time

<button id="counter" data-easy-tooltip="Count: 0">Hover me</button>

<script>
  let count = 0
  setInterval(() => {
    document.getElementById("counter").dataset.easyTooltip = `Count: ${++count}`
  }, 1000)
</script>

Smart Positioning

Tooltips automatically adapt to screen boundaries. Hover over the buttons at the corners of your screen to see smart positioning in action:

Preferred Side

Tooltips show above by default. Use data-easy-tooltip-prefer to pick a side, falling back to the opposite side when there isn't room. Using left or right switches to horizontal mode:

Above (default)

Below

Left

Right

<button data-easy-tooltip="I show above">Hover me</button>
<button data-easy-tooltip="I show below" data-easy-tooltip-prefer="below">Hover me</button>
<button data-easy-tooltip="I show on the left" data-easy-tooltip-prefer="left">Hover me</button>
<button data-easy-tooltip="I show on the right" data-easy-tooltip-prefer="right">Hover me</button>

entry uses whichever edge of the trigger your cursor crossed on the way in, so the tooltip meets you where you arrived. Keyboard focus has no entry point and falls back to above:

Entry

Come in from any edge
<div data-easy-tooltip="I show on the edge you came in from" data-easy-tooltip-prefer="entry">…</div>

Anchor Modes

By default a tooltip anchors to its trigger element. Use data-easy-tooltip-anchor to change the anchor point: cursor follows the cursor or touch point, pin freezes where the cursor is when the tooltip appears. Both have single axis versions, which take one axis from the cursor and the other from the element. Keyboard focus always falls back to the element.

Cursor

Move your cursor or tap across me

Cursor X

I track your cursor across, but stay above the box

Cursor Y

I track your cursor down the side of the box

Pin

Come in from any edge, I meet you there

Pin X

I stay where you came in across, above the box

Pin Y

I stay where you came in vertically, beside the box
<div data-easy-tooltip="I follow your cursor" data-easy-tooltip-anchor="cursor">…</div>
<div data-easy-tooltip="I snapped to the nearest edge" data-easy-tooltip-anchor="pin">…</div>

<div data-easy-tooltip="I follow you sideways" data-easy-tooltip-anchor="cursor-x">…</div>
<div data-easy-tooltip="I follow you up and down" data-easy-tooltip-anchor="cursor-y" data-easy-tooltip-prefer="right">…</div>

Events

Tooltips fire easy-tooltip-open, easy-tooltip-close, and easy-tooltip-move on their trigger element. The events bubble, so you can listen on document. move fires when a tooltip flips to another side, and when its anchor point moves. Flips are sent straight away, anchor point changes are limited to one event every 100ms. open fires when the tooltip appears, after any delay, so a hover too brief to show a tooltip fires nothing at all. The buttons below use a .5s delay to make that obvious.

Open and Close

Nothing open

Cursor

Move your cursor across me
Waiting for events
document.addEventListener("easy-tooltip-open", e => {
  console.log(e.detail.side, e.detail.point)
})

Custom Colors

Style specific tooltips using data-easy-tooltip-class and CSS variables:

Success

Danger

Info

?
<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">Delete</button>
<div data-easy-tooltip="Get help" data-easy-tooltip-class="info-tooltip">?</div>

<style>
  .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;
  }

  .info-tooltip {
    --easy-tooltip-background-color: #faf5ff;
    --easy-tooltip-border-color: #9b59b6;
    --easy-tooltip-text-color: #9b59b6;
  }
</style>

Backgrounds & Borders

Beyond flat colors, --easy-tooltip-background and --easy-tooltip-border accept any CSS image: gradients or url() images. Both follow the tooltip shape, arrow included. They can also be set per element with the data-easy-tooltip-background and data-easy-tooltip-border attributes.

Gradient Background

Image Background

Gradient Border

Rainbow Border

<button data-easy-tooltip="Gradient background" data-easy-tooltip-class="gradient-tooltip">Hover me</button>
<button data-easy-tooltip="Image background" data-easy-tooltip-background="url('photo.jpg')">Hover me</button>
<button data-easy-tooltip="Gradient border" data-easy-tooltip-border="linear-gradient(135deg, #f0f, #0ff)">Hover me</button>

<style>
  .gradient-tooltip {
    --easy-tooltip-background: linear-gradient(135deg, #667eea, #764ba2);
    --easy-tooltip-text-color: #fff;
  }
</style>

Customization Variables

Fine-tune tooltips with CSS variables:

Custom Border

Max Width

Large Arrow

Wide Arrow

Thin Arrow

No Arrow

Rounded Arrow Point

Delay

Longer Animation

Dark Theme

Slide Distance

Far From Element

<style>
  :root {
    --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-background: none;           /* Background image/gradient, replaces the background color */
    --easy-tooltip-border: none;               /* Border image/gradient, replaces the border color */
    --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 */
    --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;  /* Arrow gap from corner (for above or below tooltips) */
    --easy-tooltip-arrow-edge-buffer-y: 6px;   /* Arrow gap from corner (for left or right tooltips) */
    --easy-tooltip-arrow-radius: 0;            /* Border radius of the arrow tip */
    --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 */
  }
</style>

Per-Tooltip Adjustments

Reach for data-easy-tooltip-class when you're theming a group of tooltips, and data-easy-tooltip-style when you just need to nudge one. It writes CSS variables straight onto that tooltip's style attribute, so there's no class to name and no stylesheet to touch:

Pill

?

Swatches

Live Distance

<div data-easy-tooltip="Pill shaped, just for me" data-easy-tooltip-style="--easy-tooltip-border-radius: 999px;">?</div>
<span data-easy-tooltip="Coral" data-easy-tooltip-style="--easy-tooltip-background-color: #ff6f61; --easy-tooltip-text-color: #fff;"></span>
<span data-easy-tooltip="Teal" data-easy-tooltip-style="--easy-tooltip-background-color: #14919b; --easy-tooltip-text-color: #fff;"></span>

<div id="live-distance-area" data-easy-tooltip="Distance: 16px" data-easy-tooltip-style="--easy-tooltip-distance: 16px;">
  <input type="range" id="live-distance-slider" min="4" max="60" value="16">
</div>

<script>
  const area = document.getElementById("live-distance-area")
  const slider = document.getElementById("live-distance-slider")
  slider.addEventListener("input", () => {
    area.dataset.easyTooltip = `Distance: ${slider.value}px`
    area.dataset.easyTooltipStyle = `--easy-tooltip-distance: ${slider.value}px;`
  })
</script>