{"id":1166,"date":"2026-03-02T09:00:00","date_gmt":"2026-03-02T10:00:00","guid":{"rendered":"https:\/\/computercoursesonline.com\/?p=1166"},"modified":"2026-03-18T10:00:31","modified_gmt":"2026-03-18T10:00:31","slug":"getting-started-with-the-popover-api","status":"publish","type":"post","link":"https:\/\/computercoursesonline.com\/index.php\/2026\/03\/02\/getting-started-with-the-popover-api\/","title":{"rendered":"Getting Started With The Popover API"},"content":{"rendered":"

Getting Started With The Popover API<\/title><\/p>\n<article>\n<header>\n<h1>Getting Started With The Popover API<\/h1>\n<address>Godstime Aburu<\/address>\n<p> 2026-03-02T10:00:00+00:00<br \/>\n 2026-03-18T09:33:12+00:00<br \/>\n <\/header>\n<p>Tooltips feel like the smallest UI problem you can have. They\u2019re tiny and usually hidden. When someone asks how to build one, the traditional answer almost always comes back using some JavaScript library. And for a long time, that was the sensible advice.<\/p>\n<p>I followed it, too.<\/p>\n<p>On the surface, a tooltip is simple. Hover or focus on an element, show a little box with some text, then hide it when the user moves away. But once you ship one to real users, the edges start to show. Keyboard users <code>Tab<\/code> into the trigger, but never see the tooltip. Screen readers announce it twice, or not at all. The tooltip flickers when you move the mouse too quickly. It overlaps content on smaller screens. Pressing <code>Esc<\/code> does not close it. Focus gets lost.<\/p>\n<p>Over time, my tooltip code grew into something I didn\u2019t really want to own anymore. Event listeners piled up. Hover and focus had to be handled separately. Outside clicks needed special cases. ARIA attributes had to be kept in sync by hand. Every small fix added another layer of logic.<\/p>\n<p>Libraries helped, but they were also more like black boxes I worked around instead of fully understanding what was happening behind the scenes.<\/p>\n<p>That was what pushed me to look at the newer <a href=\"https:\/\/html.spec.whatwg.org\/multipage\/popover.html#the-popover-attribute\">Popover API<\/a>. I wanted to see what would happen if I rebuilt a single tooltip using the browser\u2019s native model without the aid of a library.<\/p>\n<p>As we start, it\u2019s worth noting that, as with any new feature, there are some things with it that are still being ironed out. That said, it currently enjoys great browser support, although there are several pieces to the overall API that are in flux. It\u2019s worth keeping an eye on <a href=\"https:\/\/caniuse.com\/?search=popover+api\">Caniuse<\/a> in the meantime.<\/p>\n<div data-audience=\"non-subscriber\" data-remove=\"true\" class=\"feature-panel-container\">\n<aside class=\"feature-panel\">\n<div class=\"feature-panel-left-col\">\n<div class=\"feature-panel-description\">\n<p>Meet <strong><a data-instant href=\"https:\/\/www.smashingconf.com\/online-workshops\/\">Smashing Workshops<\/a><\/strong> on <strong>front-end, design & UX<\/strong>, with practical takeaways, live sessions, <strong>video recordings<\/strong> and a friendly Q&A. With Brad Frost, St\u00e9ph Walter and <a href=\"https:\/\/smashingconf.com\/online-workshops\/workshops\">so many others<\/a>.<\/p>\n<p><a data-instant href=\"smashing-workshops\" class=\"btn btn--green btn--large\">Jump to the workshops \u21ac<\/a><\/div>\n<\/div>\n<div class=\"feature-panel-right-col\"><a data-instant href=\"smashing-workshops\" class=\"feature-panel-image-link\"><\/p>\n<div class=\"feature-panel-image\">\n<img loading=\"lazy\" class=\"feature-panel-image-img lazyload\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Feature Panel\" width=\"257\" height=\"355\" data-src=\"\/images\/smashing-cat\/cat-scubadiving-panel.svg\"><\/p>\n<\/div>\n<p><\/a>\n<\/div>\n<\/aside>\n<\/div>\n<h2 id=\"the-old-tooltip\">The \u201cOld\u201d Tooltip<\/h2>\n<p>Before the Popover API, using a tooltip library was not a shortcut. It was the default. Browsers didn\u2019t have a native concept of a tooltip that worked across mouse, keyboard, and assistive technology. If you cared about correctness, your only option was to use a library, and that is exactly what I did.<\/p>\n<p>At a high level, the pattern was always the same: a trigger element, a hidden tooltip element, and JavaScript to coordinate the two.<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-html\"><button class=\"info\">?<\/button>\n<div class=\"tooltip\" role=\"tooltip\">Helpful text<\/div>\n<\/code><\/pre>\n<\/div>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/getting-started-popover-api\/1-popover-api.png\"><\/p>\n<p> <img loading=\"lazy\" width=\"800\" height=\"257\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"The old approach with ~60 lines of JavaScript with five event listeners vs the new approach is about 10 lines of declarative HTML with zero event listeners.\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/getting-started-popover-api\/1-popover-api.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n The old approach required ~60 lines of JavaScript with five event listeners and manual state management. The new approach is about 10 lines of declarative HTML with zero event listeners. (<a href=\"https:\/\/files.smashing.media\/articles\/getting-started-popover-api\/1-popover-api.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<p>The library handled the wiring that allowed the element to show on hover or focus, hide on blur or mouse leave, and reposition\/resize on scroll.<\/p>\n<figure class=\"video-embed-container break-out\">\n<div class=\"video-embed-container--wrapper\"><\/div>\n<\/figure>\n<p>None of it was accidental. It was merely compensating for gaps in web platform features.<\/p>\n<h2 id=\"why-i-used-a-library\">Why I Used A Library<\/h2>\n<p>The library was doing real work for me: positioning, flipping at viewport edges, event coordination across input types, and scroll awareness inside complex layouts. Positioning alone justified the dependency. Handling scroll containers, transforms, and responsive layouts correctly is not simple.<\/p>\n<p>The real issues showed up in <strong>accessibility behavior<\/strong>, not visuals. The tooltip worked, but not all the time. Here\u2019s where things started to fray at the seams:<\/p>\n<ul>\n<li>Tooltips sometimes appeared late or not at all.<\/li>\n<li>Tabbing quickly could skip them entirely.<\/li>\n<li>Escape dismissal was not reliable.<\/li>\n<\/ul>\n<figure class=\"video-embed-container break-out\">\n<div class=\"video-embed-container--wrapper\"><\/div><figcaption>Keyboard navigation with the old implementation: Tabbing quickly causes tooltips to be skipped entirely, and Escape dismissal is unreliable.<\/figcaption><\/figure>\n<p>I also ran into issues trying to sync hover and focus behavior:<\/p>\n<ul>\n<li>Mouse users expect immediacy.<\/li>\n<li>Keyboard users expect predictability.<\/li>\n<li>Supporting both meant delays and edge cases.<\/li>\n<\/ul>\n<figure class=\"video-embed-container break-out\">\n<div class=\"video-embed-container--wrapper\"><\/div><figcaption>This timing mismatch creates an inconsistent experience across input methods.<\/figcaption><\/figure>\n<p>Not to mention, there were issues with <strong>assistive technologies<\/strong>, particularly screen readers: Sometimes the tooltip was announced, sometimes it wasn\u2019t, and sometimes it was announced twice.<\/p>\n<figure class=\"video-embed-container break-out\">\n<div class=\"video-embed-container--wrapper\"><\/div><figcaption>Screen reader behavior with custom tooltips.<\/figcaption><\/figure>\n<p>Keeping ARIA attributes in sync required manual updates. Miss one state change, and the tooltip became confusing or invisible to the accessibility tree.<\/p>\n<h2 id=\"this-was-not-bad-code\">This Was Not Bad Code<\/h2>\n<p>The implementation was tested, the library was solid, and the behavior was reasonable given the tools available at the time.<\/p>\n<blockquote><p>The core problem was not the code. It was that the web platform lacked proper affordances.<\/p><\/blockquote>\n<p>For example, the browser has no real way of knowing that the element was a tooltip. Everything was built from conventions: generic elements, event listeners, manually-managed ARIA, and custom dismissal logic.<\/p>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/getting-started-popover-api\/2-before-after-popover-api.png\"><\/p>\n<p> <img loading=\"lazy\" width=\"800\" height=\"436\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Event flow: before and after Popover API.\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/getting-started-popover-api\/2-before-after-popover-api.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n Before: A tangled web of event listeners, state management, and manual ARIA updates. After: The browser understands the relationship declaratively. (<a href=\"https:\/\/files.smashing.media\/articles\/getting-started-popover-api\/2-before-after-popover-api.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<p>Over time, the tooltip could become fragile. Small changes carried risk. Minor fixes caused regressions. Worse, adding new tooltips inherited the same complexity. Things technically worked, but never felt settled or complete.<\/p>\n<p>That was the state of things when I decided to rebuild the tooltip using the browser\u2019s native <a href=\"https:\/\/html.spec.whatwg.org\/multipage\/popover.html#the-popover-attribute\">Popover API<\/a>.<\/p>\n<div class=\"partners__lead-place\"><\/div>\n<h2 id=\"the-moment-i-tried-the-popover-api\">The Moment I Tried The Popover API<\/h2>\n<p>I didn\u2019t switch to using the Popover API because I wanted to experiment with something new. I switched because I was tired of maintaining tooltip behavior that I believed the browser should have already understood.<\/p>\n<p>I was skeptical at first. Most new web APIs promise simplicity, but still require glue, edge-case handling, or fallback logic that quietly recreates the same complexity that you were trying to escape.<\/p>\n<p>So, I tried the Popover API in the smallest way possible. Here\u2019s what that looked like:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-html\"><!-- popovertarget creates the connection to id=\"tip-1\" -->\n<button popovertarget=\"tip-1\">?<\/button>\n\n<!-- popover=\"manual\": browser manages this as a popover -->\n<!-- role=\"tooltip\": tells assistive technology what this is -->\n<div id=\"tip-1\" popover=\"manual\" role=\"tooltip\">\n This button triggers a helpful tip.\n<\/div>\n<\/code><\/pre>\n<\/div>\n<figure class=\"video-embed-container break-out\">\n<div class=\"video-embed-container--wrapper\"><\/div><figcaption>The complete tooltip implementation using the Popover API<\/figcaption><\/figure>\n<p>No event listeners. No state tracking. No ARIA updates handled in JavaScript. I focused the button, and the tooltip appeared. I pressed the <code>Esc<\/code> key, and it disappeared.<\/p>\n<h2 id=\"what-immediately-stood-out\">What Immediately Stood Out<\/h2>\n<p>A few things became obvious within minutes:<\/p>\n<h3 id=\"i-didn-t-write-any-javascript-to-open-or-close-it\">I Didn\u2019t Write Any JavaScript To Open Or Close It<\/h3>\n<p>The browser handled invocation entirely through HTML. The relationship between trigger and tooltip was explicit.<\/p>\n<h3 id=\"the-esc-key-just-worked\">The <code>Esc<\/code> Key Just Worked<\/h3>\n<p>I didn\u2019t add a key listener. Pressing the <code>Esc<\/code> key properly closed the tooltip because the browser understands that popovers should be dismissible.<\/p>\n<h3 id=\"aria-state-automatically-synced\">ARIA State Automatically Synced<\/h3>\n<p>The <code>aria-expanded<\/code> attribute updated on its own when the popover opened and closed. There was no need for manual bookkeeping and no risk of stale state.<\/p>\n<figure class=\"video-embed-container break-out\">\n<div class=\"video-embed-container--wrapper\"><\/div><figcaption>The browser\u2019s DevTools showing <code>aria-expanded<\/code> automatically updating from <code>false<\/code> to <code>true<\/code> as the popover opens.<\/figcaption><\/figure>\n<p>This was the moment that the Popover API stopped feeling like a convenience and more like true bona fide platform behavior.<\/p>\n<p>What surprised me most was not the reduced code but the <strong>change in responsibility<\/strong>. Before, the tooltip existed because my JavaScript said so. Now, it exists because the browser understands what it is supposed to be and its role in the markup. The tooltip is no longer simply a box positioned near a button anymore, but participating in the browser\u2019s focus model, the accessibility tree, and native dismissal rules.<\/p>\n<p>That\u2019s when my migration to the Popover API started.<\/p>\n<h3 id=\"understanding-invoker-commands\">Understanding Invoker Commands<\/h3>\n<p>The <code>popovertarget<\/code> and <code>popovertargetaction<\/code> attributes are part of HTML\u2019s invoker commands, a declarative way to control interactive elements without JavaScript.<\/p>\n<ul>\n<li><code>popovertarget="id"<\/code>: Connects the button to a popover element.<\/li>\n<li><code>popovertargetaction<\/code>: Specifies what should happen:\n<ul>\n<li><code>show<\/code>: Only opens the popover.<\/li>\n<li><code>hide<\/code>: Only closes the popover.<\/li>\n<li><code>toggle<\/code>(default): Opens the popover if closed and closes it if it\u2019s open.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>This means you can have multiple triggers for the same tooltip:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-html\"><button popovertarget=\"help-tip\" popovertargetaction=\"show\">\n Show Help\n<\/button>\n\n<button popovertarget=\"help-tip\" popovertargetaction=\"hide\">\n Close Help\n<\/button>\n\n<div id=\"help-tip\" popover=\"manual\" role=\"tooltip\">\n Help content\n<\/div>\n<\/code><\/pre>\n<\/div>\n<p>The browser coordinates everything with no JavaScript needed for the basic interaction.<\/p>\n<h2 id=\"free-accessibility-wins\">Free Accessibility Wins<\/h2>\n<p>This is the part that made me switch completely. I expected the Popover API to reduce code. I didn\u2019t expect it to remove entire categories of accessibility bugs I had been chasing for years. Before the migration, my tooltip system looked fine at the very least. Keyboard support existed, ARIA attributes were present, and screen readers usually behaved accordingly. But \u201cusually\u201d did a lot of heavy lifting.<\/p>\n<p>Once I swapped in native popovers, three things changed immediately.<\/p>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/getting-started-popover-api\/3-accessibility-tree-comparison.png\"><\/p>\n<p> <img loading=\"lazy\" width=\"800\" height=\"436\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Accessibility tree comparison: Custom vs Native Popover API.\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/getting-started-popover-api\/3-accessibility-tree-comparison.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n Custom implementations use fragile JavaScript to connect triggers and tooltips. The Popover API creates a native browser connection that assistive technology can trust. (<a href=\"https:\/\/files.smashing.media\/articles\/getting-started-popover-api\/3-accessibility-tree-comparison.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<h3 id=\"1-the-keyboard-just-works\">1. The Keyboard \u201cJust Works\u201d<\/h3>\n<p>Keyboard support depended on multiple layers lining up correctly: focus had to trigger the tooltip, blur had to hide it, <code>Esc<\/code> had to be wired manually, and timing mattered. If you missed one edge case, the tooltip would either stay open too long or disappear before it could be read.<\/p>\n<p>With the <code>popover<\/code> attribute set to <code>auto<\/code> or <code>manual<\/code>, the browser takes over the basics: <code>Tab<\/code> and <code>Shift<\/code>+<code>Tab<\/code> behave normally, <code>Esc<\/code> closes the tooltip every time, and no extra listeners are required.<\/p>\n<pre><code class=\"language-html\"><div popover=\"manual\">\n Helpful explanation\n<\/div>\n<\/code><\/pre>\n<p>What disappeared from my codebase were global keydown handlers, <code>Esc<\/code>-specific cleanup logic, and state checks during keyboard navigation. The keyboard experience stopped being something I had to maintain, and it became a browser guarantee.<\/p>\n<h3 id=\"2-screenreader-predictability\">2. Screenreader Predictability<\/h3>\n<p>This was the biggest improvement. Even with careful ARIA work, the behavior varied, as I outlined earlier. Every small change felt risky. Using a popover with a proper role looks and feels a lot more stable and predictable as far as what\u2019s going to happen:<\/p>\n<pre><code class=\"language-html\"><div popover=\"manual\" role=\"tooltip\">\n Helpful explanation\n<\/div>\n<\/code><\/pre>\n<p>And here\u2019s another win: After the switch, <a href=\"https:\/\/www.smashingmagazine.com\/2024\/11\/why-optimizing-lighthouse-score-not-enough-fast-website\/\">Lighthouse<\/a> stopped flagging incorrect ARIA state warnings for the interaction, largely because there are no longer custom ARIA states for me to accidentally get wrong.<\/p>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/getting-started-popover-api\/4-manual-aria-popover-api.png\"><\/p>\n<p> <img loading=\"lazy\" width=\"800\" height=\"436\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"ARIA state warnings before migration, and 100% audit score after switching to the Popover API.\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/getting-started-popover-api\/4-manual-aria-popover-api.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n Before the migration, Lighthouse flagged accessibility warnings about incorrect ARIA state management. After switching to the Popover API, the audit score improved. (<a href=\"https:\/\/files.smashing.media\/articles\/getting-started-popover-api\/4-manual-aria-popover-api.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<h3 id=\"3-focus-management\">3. Focus Management<\/h3>\n<p>Focus used to be fragile. Before, I had rules like: let focus trigger show tooltip, move focus into tooltip and don\u2019t close, blur trigger when it\u2019s too close, and close tooltip and restore focus manually. This worked until it didn\u2019t.<\/p>\n<p>With the Popover API, the browser enforces a simpler model where focus can more naturally move into the popover. Closing the popover returns focus to the trigger, and there are no invisible focus traps or lost focus moments. And I didn\u2019t add focus restoration code; I removed it.<\/p>\n<figure class=\"video-embed-container break-out\">\n<div class=\"video-embed-container--wrapper\"><\/div><figcaption>Tab to focus the trigger, the tooltip appears, press <code>Escape<\/code> to dismiss, and focus automatically returns to the trigger.<\/figcaption><\/figure>\n<h2 id=\"where-the-popover-api-maybe-still-isn-t-enough\">Where The Popover API Maybe Still Isn\u2019t Enough<\/h2>\n<p>As much as the Popover API has simplified my code and improved semantics, it still has not completely eliminated JavaScript. That\u2019s not totally a bad thing because what\u2019s changed is that JavaScript is no longer a key dependency. I am no longer compensating for missing platform behavior anymore. I am much more focused on <em>intent<\/em>.<\/p>\n<p>Here are a few places where I could see the API continue to improve.<\/p>\n<h3 id=\"tooltip-timing-still-matters\">Tooltip Timing Still Matters<\/h3>\n<p>Native popovers open and close immediately. That is usually the expected behavior, but not always ideal for what we consider to be tooltips. In those cases, instant dismissal can feel unstable when you move your mouse a few pixels too quickly or accidentally brush past the trigger — the tooltip will flash, then disappear, which can be jarring.<\/p>\n<p>I want to be able to control that timing and apply delays between hover or focus and opening the tooltip. So I still add small delays. What changed was how much of the interaction logic I actually needed to own. Before, even basic open and close behavior required JavaScript. With the Popover API, and especially with HTML invoker commands, that responsibility shifts back to the browser.<\/p>\n<pre><code class=\"language-html\"><button\n popovertarget=\"help-tip\"\n popovertargetaction=\"show\">\n ?\n<\/button>\n\n<div id=\"help-tip\" popover=\"manual\" role=\"tooltip\">\n This button triggers a helpful tip.\n<\/div>\n<\/code><\/pre>\n<p>At this point, the browser handles invocation, dismissal, and ARIA state on its own. There\u2019s no JavaScript involved just to make the tooltip appear or disappear.<\/p>\n<p>JavaScript only comes back in when I want intentional behavior. In this case, a short delay before hiding the tooltip, and cancelling if the pointer moves into it. This isn\u2019t about accessibility fixes. It\u2019s about human behavior.<\/p>\n<p>It\u2019s worth noting that CSS is beginning to explore this space as well. The emerging interest\/invoker work introduces <a href=\"https:\/\/css-tricks.com\/a-first-look-at-the-interest-invoker-api-for-hover-triggered-popovers\/#aa-interest-delay-and-the-css-of-it-all\">ways to express entry and exit delays directly in CSS<\/a>, which could remove this small bit of JavaScript entirely. For now, I still handle it imperatively, but the direction of the platform is clear.<\/p>\n<pre><code class=\"language-javascript\">let hideTimeout;\n\nconst show = () => {\n clearTimeout(hideTimeout);\n tooltip.showPopover();\n};\n\nconst hide = () => {\n hideTimeout = setTimeout(() => {\n tooltip.hidePopover();\n }, 200);\n};\n<\/code><\/pre>\n<p>The difference is that this logic stays small and local. It no longer defines how the tooltip works. It simply refines how it feels.<\/p>\n<h3 id=\"hover-intent-with-invoker-commands\">Hover Intent With Invoker Commands<\/h3>\n<p>The browser does not know why someone hovers over an element or focuses on it. Was it intentional, or was the pointer just passing through? That part has always required some judgment.<\/p>\n<p>What changed is where that logic lives. With invoker commands handling the core open and close behavior, JavaScript no longer owns the interaction model. It only adds intent on top of it.<\/p>\n<pre><code class=\"language-html\"><button\n popovertarget=\"help-tip\"\n popovertargetaction=\"show\">\n ?\n<\/button>\n<\/code><\/pre>\n<p>The platform manages invocation, dismissal, and ARIA state. JavaScript is only needed when we want behavior that the browser cannot infer, such as a short delay before hiding or cancelling dismissal if the pointer moves into the tooltip.<\/p>\n<pre><code class=\"language-javascript\">let hideTimeout;\n\nconst show = () => {\nclearTimeout(hideTimeout);\n tooltip.showPopover();\n};\n\nconst hide = () => {\n hideTimeout = setTimeout(() => {\n tooltip.hidePopover();\n }, 200);\n};\n<\/code><\/pre>\n<p>And again, CSS is beginning to explore this space with new interaction primitives, which may reduce the need for custom hover intent code even further.<\/p>\n<h3 id=\"manual-popovers-and-focus\">Manual Popovers And Focus<\/h3>\n<p>For <code>popover="manual"<\/code>, the browser does not restore focus automatically the way it can for auto popovers. That responsibility remains explicit. When a tooltip opens on focus and closes on blur, I return focus deliberately to the trigger:<\/p>\n<pre><code class=\"language-javascript\">tooltip.hidePopover();\ntrigger.focus();\n<\/code><\/pre>\n<p>This is not a limitation but a clear boundary between platform behavior and person intent.<\/p>\n<h3 id=\"the-honest-take\">The Honest Take<\/h3>\n<p>The Popover API does not magically solve tooltips. It stopped forcing me to rebuild fragile infrastructure. I still write JavaScript and think about edge cases, but now I am solving product problems instead of recreating UI primitives the browser should already understand.<\/p>\n<div class=\"partners__lead-place\"><\/div>\n<h2 id=\"when-i-would-still-reach-for-a-tooltip-library\">When I would Still Reach For A Tooltip Library<\/h2>\n<p>Even after migrating my tooltips to the Popover API, I did not walk away thinking libraries were old and obsolete. They have earned their place, just in more specific situations.<\/p>\n<h3 id=\"1-large-or-mature-design-systems\">1. Large Or Mature Design Systems<\/h3>\n<p>If you are maintaining a large design system used across multiple teams, a tooltip library can still make sense because centralized behavior, documented patterns, and consistent defaults across products. In those environments, changing the underlying interaction model is not just a technical decision; it is an organizational one. A well-maintained library gives teams guardrails, especially when not everyone is deeply familiar with accessibility nuances.<\/p>\n<h3 id=\"2-complex-positioning-requirements\">2. Complex Positioning Requirements<\/h3>\n<p>For most tooltips, native positioning is enough, but if you need collision detection across nested scroll containers, custom flipping logic, or fine-grained control over offsets and boundaries, libraries like <a href=\"https:\/\/floating-ui.com\">Floating UI<\/a> still shine. They are optimized for geometry problems that the platform is only beginning to address.<\/p>\n<p>It is also worth mentioning <a href=\"https:\/\/css-tricks.com\/css-anchor-positioning-guide\/\">CSS anchor positioning<\/a>, which is starting to cover many of the problems that tooltip libraries historically solved. Anchors allow a popover to be positioned relative to a trigger using pure CSS, including viewport-aware placement and edge flipping. This moves even more responsibility back to the platform instead of JavaScript.<\/p>\n<p>That said, anchor positioning is still new and <a href=\"https:\/\/css-tricks.com\/css-anchor-positioning-guide\/#aa-known-bugs\">there are known issues,<\/a> although the good news is that they are part of Interop, meaning <a href=\"https:\/\/webstatus.dev\/features\/popover?q=baseline_date%3A2025-01-01..2025-12-31&start=25\">we can look forward to full and consistent browser support<\/a>. For teams that need consistent cross-browser behavior today, libraries remain the practical choice. The direction is clear that the platform is steadily absorbing work that once required dedicated positioning engines.<\/p>\n<h3 id=\"3-teams-without-accessibility-experience\">3. Teams Without Accessibility Experience<\/h3>\n<p>This one matters. If a team does not have strong accessibility knowledge, a good library can act as a safety net, though it will not guarantee perfect accessibility. It can, however, prevent the many common mistakes. The Popover API gives you better defaults, but it still assumes you know when to add roles, labels, focus management, and testing. Without that understanding, even native tools can be misused.<\/p>\n<h2 id=\"the-decision-line\">The Decision Line<\/h2>\n<p>For me, the choice now looks like this:<\/p>\n<blockquote class=\"pull-quote\">\n<p>\n <a class=\"pull-quote__link\" aria-label=\"Share on Twitter\" href=\"https:\/\/twitter.com\/share?text=%0aUse%20the%20Popover%20API%20for%20simplicity,%20clarity,%20and%20platform-aligned%20behavior.%20Use%20a%20library%20when%20scale,%20customization,%20or%20constraints%20demand%20it.%20It%e2%80%99s%20not%20about%20purity.%20It%e2%80%99s%20about%20choosing%20the%20right%20level%20of%20abstraction%20for%20the%20problem%20in%20front%20of%20you.%0a&url=https:\/\/smashingmagazine.com%2f2026%2f03%2fgetting-started-popover-api%2f\"><\/p>\n<p>Use the Popover API for simplicity, clarity, and platform-aligned behavior. Use a library when scale, customization, or constraints demand it. It\u2019s not about purity. It\u2019s about choosing the right level of abstraction for the problem in front of you.<\/p>\n<p> <\/a>\n <\/p>\n<div class=\"pull-quote__quotation\">\n<div class=\"pull-quote__bg\">\n <span class=\"pull-quote__symbol\">\u201c<\/span><\/div>\n<\/p><\/div>\n<\/blockquote>\n<p>And sometimes the right tool is still a library — just no longer by default.<\/p>\n<figure class=\"\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/getting-started-popover-api\/5-popover-api-browser-support.png\"><\/p>\n<p> <img loading=\"lazy\" width=\"800\" height=\"800\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/getting-started-popover-api\/5-popover-api-browser-support.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n (<a href=\"https:\/\/files.smashing.media\/articles\/getting-started-popover-api\/5-popover-api-browser-support.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>The Popover API means that tooltips are no longer something you simulate. They\u2019re something the browser understands. Opening, closing, keyboard behavior, Escape handling, and a big chunk of accessibility now come from the platform itself, not from ad-hoc JavaScript.<\/p>\n<p>That does not mean tooltip libraries are obsolete because they still make sense for complex design systems, heavy customization, or legacy constraints, but <strong>the default has shifted<\/strong>. For the first time, the simplest tooltip can also be the most correct one. If you are curious, try this experiment: Simply replace just one tooltip in your product with the Popover API, do not rewrite everything, do not migrate a whole system, and just pick one and see what disappears from your code.<\/p>\n<p>When the platform gives you a better primitive, the win is not just fewer lines of JavaScript, but it is fewer things you have to worry about at all.<\/p>\n<p>Check out the full source code in <a href=\"https:\/\/github.com\/BboyGT\/PopOver-API\">my GitHub repo<\/a>.<\/p>\n<h3 id=\"further-reading\">Further Reading<\/h3>\n<p>For deeper dives into popovers and related APIs:<\/p>\n<ul>\n<li>\u201c<a href=\"https:\/\/css-tricks.com\/poppin-in\/\">Poppin\u2019 In<\/a>\u201d, Geoff Graham<\/li>\n<li>\u201c<a href=\"https:\/\/css-tricks.com\/clarifying-the-relationship-between-popovers-and-dialogs\/\">Clarifying the Relationship Between Popovers and Dialogs<\/a>\u201d, Zell Liew<\/li>\n<li>\u201c<a href=\"https:\/\/una.im\/popover-hint\/\">What is popover=hint?<\/a>\u201d, Una Kravets<\/li>\n<li>\u201c<a href=\"https:\/\/css-tricks.com\/invoker-commands-additional-ways-to-work-with-dialog-popover-and-more\/\">Invoker Commands<\/a>\u201d, Daniel Schwarz<\/li>\n<li>\u201c<a href=\"https:\/\/css-tricks.com\/creating-an-auto-closing-notification-with-an-html-popover\/\">Creating an Auto-Closing Notification with an HTML Popover<\/a>\u201d, Preethi<\/li>\n<li><a href=\"https:\/\/open-ui.org\/components\/popover.research.explainer\/\">Open UI Popover API Explainer<\/a><\/li>\n<li>\u201c<a href=\"https:\/\/css-tricks.com\/popover-the-balloons\/\">Pop(over) the Balloons<\/a>\u201d, John Rhea<\/li>\n<li>\u201c<a href=\"https:\/\/css-tricks.com\/css-anchor-positioning-guide\/\">CSS Anchor Positioning<\/a>\u201d, Juan Diego Rodr\u00edguez<\/li>\n<\/ul>\n<p>MDN also <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Popover_API\">offers comprehensive technical documentation<\/a> for the Popover API.<\/p>\n<div class=\"signature\">\n <img src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Smashing Editorial\" width=\"35\" height=\"46\" loading=\"lazy\" class=\"lazyload\" data-src=\"https:\/\/www.smashingmagazine.com\/images\/logo\/logo--red.png\"><br \/>\n <span>(gg, yk)<\/span>\n<\/div>\n<\/article>\n","protected":false},"excerpt":{"rendered":"<p>Getting Started With The Popover API Getting Started With The Popover API Godstime Aburu 2026-03-02T10:00:00+00:00 2026-03-18T09:33:12+00:00 Tooltips feel like the smallest UI problem you can have. They\u2019re tiny and usually hidden. When someone asks how to build one, the traditional answer almost always comes back using some JavaScript library. And for a long time, that…<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[11],"tags":[],"_links":{"self":[{"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/posts\/1166"}],"collection":[{"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/comments?post=1166"}],"version-history":[{"count":1,"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/posts\/1166\/revisions"}],"predecessor-version":[{"id":1167,"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/posts\/1166\/revisions\/1167"}],"wp:attachment":[{"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/media?parent=1166"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/categories?post=1166"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/tags?post=1166"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}