Lightweight Tooltips with CSS Variable Styling – ez-tip

Category: Javascript , Tooltip | November 24, 2025
Author:graphieros
Views Total:46 views
Official Page:Go to website
Last Update:November 24, 2025
License:MIT

Preview:

Lightweight Tooltips with CSS Variable Styling – ez-tip

Description:

ez-tip is a lightweight TypeScript/JavaScript library that adds customizable tooltip popups to HTML elements using HTML data attributes.

The library handles the logic for showing, hiding, and positioning tooltips, leaving the visual styling entirely up to you via CSS custom properties.

Features:

  • Zero dependencies – No jQuery, no bloat, just vanilla JavaScript
  • CSS variable styling – Complete control over appearance without touching JS
  • Smart positioning – Automatically positions tooltips to remain visible
  • Auto-updating – Repositions on scroll, resize, and element changes
  • Configurable delay – Control tooltip appearance timing per-instance or globally
  • Auto-cleanup – Handles DOM removal without memory leaks

How to use it:

1. Install ez-tip and import ‘render’ & ‘config’ (OPTIONAL) functions into your project.

# Yarn
$ yarn add ez-tip
# NPM
$ npm install ez-tip
import { render, config } from "ez-tip";
// OR
<script type="module">
  import { render,config } from "./dist/ez-tip.js";
</script>

2. Add the data-ez-tip attribute to any element you want a tooltip on. The attribute’s value is the tooltip content (HTML is supported).

<span 
  data-ez-tip="<em>HTML</em> is allowed!">
  Hover Me
</span>

3. Call the render function and pass a global debounce delay (ms) for repositioning updates. The delay option controls the debounce interval for scroll/resize handlers that trigger repositioning. It doesn’t affect the hover delay for showing the tooltip.

render({ 
  delay: 50
});

4. ez-tip only provides the JavaScript logic. You must provide CSS to style the tooltips. The library uses specific CSS class names and data attributes for targeting. Here’s a minimal base:

/* Define default appearance using CSS variables */
:root {
  /* These are the variables ez-tip looks for */
  --ez-tooltip-background-color: #333; 
  --ez-tooltip-color: #fff; 
  --ez-tooltip-padding: 0.5rem 0.75rem; 
  --ez-tooltip-border-radius: 4px; 
  /* You need to define this one yourself for the arrow */
  --tooltip-arrow-size: 5px;
}
.ez-tip {
  /* Base styles */
  position: fixed; /* The library sets this inline, but good to know */
  background-color: var(--ez-tooltip-background-color);
  color: var(--ez-tooltip-color);
  padding: var(--ez-tooltip-padding);
  border-radius: var(--ez-tooltip-border-radius);
  z-index: 1000; /* Or whatever suits your stacking context */
  pointer-events: none; /* Tooltip shouldn't interfere with mouse */
  /* Visibility transition */
  opacity: 0;
  transition: opacity 0.15s ease-in-out;
  /* You might want transform transitions too */
}
.ez-tip.ez-tip-visible {
  opacity: 1;
}
/* Arrow styling */
.ez-tip::after {
  content: "";
  position: absolute;
  width: 0;
  height: 0;
  border-style: solid;
}
/* Arrow positioning based on data-position attribute */
.ez-tip[data-position="top"]::after {
  /* Points down */
  bottom: calc(-1 * var(--tooltip-arrow-size));
  left: 50%;
  transform: translateX(-50%);
  border-width: var(--tooltip-arrow-size) var(--tooltip-arrow-size) 0 var(--tooltip-arrow-size);
  border-color: var(--ez-tooltip-background-color) transparent transparent transparent;
}
.ez-tip[data-position="bottom"]::after {
   /* Points up */
  top: calc(-1 * var(--tooltip-arrow-size));
  left: 50%;
  transform: translateX(-50%);
  border-width: 0 var(--tooltip-arrow-size) var(--tooltip-arrow-size) var(--tooltip-arrow-size);
  border-color: transparent transparent var(--ez-tooltip-background-color) transparent;
}
.ez-tip[data-position="left"]::after {
  /* Points right */
  right: calc(-1 * var(--tooltip-arrow-size));
  top: 50%;
  transform: translateY(-50%);
  border-width: var(--tooltip-arrow-size) 0 var(--tooltip-arrow-size) var(--tooltip-arrow-size);
  border-color: transparent transparent transparent var(--ez-tooltip-background-color);
}
.ez-tip[data-position="right"]::after {
  /* Points left */
  left: calc(-1 * var(--tooltip-arrow-size));
  top: 50%;
  transform: translateY(-50%);
  border-width: var(--tooltip-arrow-size) var(--tooltip-arrow-size) var(--tooltip-arrow-size) 0;
  border-color: transparent var(--ez-tooltip-background-color) transparent transparent;
}

5. You can also override the default styles globally using config:

config.backgroundColor = "#FFFFFF";
config.color = "#1A1A1A";
config.padding = "0 0.5rem";
config.borderRadius = "3px";

6. Control the behavior & appearance of the toolips using the following HTML data attributes:

  • data-ez-tip: Required. Sets the tooltip content (text or HTML).
  • data-ez-tip-hover: Controls visibility. Set to "false" for an always-visible tooltip; defaults to true (or attribute absence) for hover/focus trigger.
  • data-ez-tip-delay: Specifies the hover show delay in milliseconds (e.g., "250").
  • data-ez-tip-position: Sets the preferred placement (top, bottom, left, right). The library attempts this first but will auto-fallback if it doesn’t fit.
  • data-ez-tip-offset: Defines the distance in pixels between the tooltip and the target element (e.g., "12").
  • data-ez-tip-background: Overrides the background color for a single tooltip (e.g., "#ff0000" or "red").
  • data-ez-tip-color: Overrides the text color for a single tooltip (e.g., "#ffffff" or "white").
  • data-ez-tip-padding: Overrides the padding for a single tooltip (e.g., "1rem").
  • data-ez-tip-border-radius: Overrides the border radius for a single tooltip (e.g., "0").
  • data-ez-tip-hover-lock: Keep the tooltip hoverable.
<span 
  data-ez-tip="<em>HTML</em> is allowed!"
  data-ez-tip-hover="true"
  data-ez-tip-delay="100"
  data-ez-tip-offset="10"
  data-ez-tip-position="right"
  data-ez-tip-background="#222222"
  data-ez-tip-color="#FAFAF7"
  data-ez-tip-padding="20px"
  data-ez-tip-border-radius="10px"
  data-ez-tip-hover-lock="true"> 
  Hover Me 
</span>

FAQs:

Q: How exactly does the ‘smart’ positioning choose the best placement?

A: It first checks if you specified data-ez-tip-position. If yes, it tries that first. If not, or if the preferred/first position doesn’t fit within the viewport boundaries (simple check: top >= 0, left >= 0, bottom <= window.innerHeight, right <= window.innerWidth), it iterates through a predefined order (top, bottom, right, left). It uses the first position in that sequence that fits within the viewport. If, theoretically, none of the positions fit perfectly (e.g., huge tooltip on a small screen near the edge), it falls back to using the preferred position (if set) or ‘top’ by default, even if it overflows.

Q: How does it handle elements added to the DOM dynamically (e.g., after an AJAX call)?

A: The render() function scans the DOM once on DOMContentLoaded. It won’t automatically pick up elements added later. If you add elements with data-ez-tip attributes after the initial load, you’ll need to either call render() again (which might re-initialize existing tooltips unnecessarily) or adapt the library’s internal logic (A function) to target only the new elements. The library doesn’t expose a method for single-element initialization out of the box.

Changelog:

v0.2.2 (11/24/2025)

  • Set pointer-events to ‘auto’ on permanent tooltips, to keep their content selectable.

v0.2.1 (11/23/2025)

  • Ensure the render function runs immediately if the DOM is already ready instead of waiting for DOMContentLoaded

v0.2.0 (11/21/2025)

  • Use the data-ez-tip-hover-lock=”true” on the target to keep the tooltip hoverable

v0.1.2 (05/03/2025)

  • Add option to set custom css classes

v0.1.1 (05/03/2025)

  • Improve tooltip placement when target element overflows viewport.

You Might Be Interested In:


Leave a Reply