DEV Community

Nick Benksim
Nick Benksim

Posted on • Originally published at csscodelab.com

Creating Tooltips Without Using JavaScript

The Tooltip Trap: Why are we still using JS?

Grab a latte and pull up a chair, because we need to talk about one of the most over-engineered components in web history: the tooltip. For years, the moment a designer added a "hover for info" bubble to a Figma file, frontend devs would immediately reach for npm install tippy.js or some other 15kb library. But here is the thing: in 2026, if you are using JavaScript to position a simple text bubble, you are essentially using a sledgehammer to crack a nut. We are Senior devs; we should be obsessed with performance and keeping the main thread clean. Let’s look at how we can achieve bulletproof tooltips using nothing but pure CSS.

How we suffered before: The Hacky Era

Remember the dark ages? We used to rely on data-attributes and pseudo-elements (::before and ::after). It felt clever at the time: you’d store the text in content: attr(data-tooltip) and use absolute positioning. But the moment that tooltip hit the edge of the viewport, it would get clipped. Or worse, if a parent container had overflow: hidden, your tooltip simply vanished into the void.

We spent hours tweaking z-index values, praying that the tooltip wouldn't slide under a sticky header. If you want to see how we eventually solved those broader layout struggles, you should check out our guide on Logical properties of CSS which helped us stop thinking in "left/right" and start thinking in "start/end". But even with logical properties, the positioning of tooltips remained a nightmare until very recently.

The modern way in 2026: Anchors and Popovers

Welcome to the future. The "Modern Way" is built on two pillars: the Popover API and CSS Anchor Positioning. The Popover API allows us to move the tooltip into the "top layer" (meaning no more z-index or overflow battles), while Anchor Positioning lets us glue the tooltip to its trigger without a single line of getBoundingClientRect().

By using the anchor() function, we can tell the browser: "Hey, take this tooltip and place its bottom edge exactly 10px above this specific button." The browser handles the math, the window resizing, and the edge detection. For a deep dive into the syntax, you might want to read more about Anchor Positioning in CSS, but for now, let's look at the code.

Ready-to-use code snippet


/* The Trigger */
.anchor-button {
  anchor-name: --my-tooltip;
  padding: 10px 20px;
}

/* The Tooltip (using Popover API) */
.tooltip {
  position-anchor: --my-tooltip;
  position: absolute;
  top: anchor(top);
  left: anchor(center);
  transform: translate(-50%, calc(-100% - 10px));
  
  /* Styling magic */
  background: #333;
  color: white;
  padding: 8px 12px;
  border-radius: 6px;
  font-size: 14px;
  pointer-events: none;
  border: none;
}

/* Show logic without JS */
.anchor-button:hover + .tooltip {
  display: block;
}

In the HTML, you just need a button and a div with a popover attribute. The browser takes care of the rest. No recalculating offsets on scroll, no heavy scripts—just pure, declarative layout engine power.

Common beginner mistake

The biggest mistake I see mid-level devs make when transitioning to pure CSS tooltips is forgetting about accessibility (A11y). Just because you aren't using JS doesn't mean you can ignore the keyboard user. If your tooltip only triggers on :hover, a user tabbing through your site will never see it. Always ensure your tooltip displays on :focus-within or :focus as well. Also, don't forget to link the tooltip to the button using aria-describedby. A Senior dev knows that a UI isn't "awesome" if it only works for people with a mouse.

🔥 We publish more advanced CSS tricks, ready-to-use snippets, and tutorials in our Telegram channel. Subscribe so you don't miss out!

Top comments (0)