{"id":2027,"date":"2024-05-06T10:50:59","date_gmt":"2024-05-06T16:50:59","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=2027"},"modified":"2024-06-24T13:55:14","modified_gmt":"2024-06-24T19:55:14","slug":"using-the-popover-api-for-html-tooltips","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/using-the-popover-api-for-html-tooltips\/","title":{"rendered":"Using the Popover API for HTML Tooltips"},"content":{"rendered":"\n<p>Can it be done? This plucky front-end developer intends to find out.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"633\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/CleanShot-2024-05-06-at-08.57.55%402x.png?resize=1024%2C633&#038;ssl=1\" alt=\"\" class=\"wp-image-2063\" style=\"width:576px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/CleanShot-2024-05-06-at-08.57.55%402x.png?resize=1024%2C633&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/CleanShot-2024-05-06-at-08.57.55%402x.png?resize=300%2C185&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/CleanShot-2024-05-06-at-08.57.55%402x.png?resize=768%2C475&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/CleanShot-2024-05-06-at-08.57.55%402x.png?resize=1536%2C950&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/CleanShot-2024-05-06-at-08.57.55%402x.png?w=1666&amp;ssl=1 1666w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">Can we do it?<\/figcaption><\/figure>\n<\/div>\n\n\n<p><a href=\"https:\/\/frontendmasters.com\/blog\/popover-api-is-here\/\">We looked at the Popover API<\/a> and how it&#8217;s made it&#8217;s way across all browsers already just last week. One of the things I <em>should<\/em> have done is looked at the accessibility considerations more closely. Thanks to Melanie Sumner there is <a href=\"https:\/\/codepen.io\/melsumner\/pen\/VwNmYLY\">a great explainer with demos<\/a>. I tried to adhere to the points made in there the best I could while making a classic tooltips experience, and we&#8217;ll do a bit of a review at the end where deviations happened.<\/p>\n\n\n<div class=\"box article-series\">\n  <header>\n    <h3 class=\"article-series-header\">Article Series<\/h3>\n  <\/header>\n  <div class=\"box-content\">\n            <ol>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/using-the-popover-api-for-html-tooltips\/\">Using the Popover API for HTML Tooltips<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/footnotes-progressively-enhanced-to-popovers\/\">Footnotes Progressively Enhanced to Popovers<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/popovers-work-pretty-nicely-as-slide-out-drawers\/\">Popovers Work Pretty Nicely as Slide-Out Drawers<\/a>\n            <\/li>\n                  <\/ol>\n        <\/div>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Starting With HTML<\/h2>\n\n\n\n<p>This is actually my favorite part, as remember, this API can be used <em>entirely<\/em> in HTML. That rules. Remember when we got <code>&lt;details&gt;<\/code> where you could build an interactive open\/close disclosure widget thing with just HTML? That also rules, but this is even cooler. <\/p>\n\n\n\n<p>One small weirdness though, it only works in HTML alone when a <code>&lt;button&gt;<\/code> is the &#8220;invoker&#8221; of opening the popup. So in the case of a <em>tooltip<\/em>, that button might come right in the middle of a sentence like.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n   This blog post was written by \n   <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">popovertarget<\/span>=<span class=\"hljs-string\">\"popover-chris-coyier\"<\/span>&gt;<\/span>\n     Chris Coyier\n   <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n   the genius.\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Then elsewhere in the DOM (the location of which doesn&#8217;t effect the core functionality):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"popover-chris-coyier\"<\/span> <span class=\"hljs-attr\">popover<\/span> <span class=\"hljs-attr\">role<\/span>=<span class=\"hljs-string\">\"tooltip\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"\/images\/chris.jpg\"<\/span> <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">\"A portrait of Chris Coyier in a tuxedo.\"<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"100\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"100\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h4<\/span>&gt;<\/span>Chris Coyier<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h4<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Handsome fella.<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Note I&#8217;m calling this an <em>HTML<\/em> Tooltip, because what pops up isn&#8217;t text alone, which you might argue can be done with the <code>title<\/code> attribute. These popovers are far more flexible, allowing you to style them and can contain anything HTML can. <\/p>\n\n\n\n<p>The weird part here is the button smack in the middle of the paragraph. That&#8217;ll be a bit awkward styling-wise, but that&#8217;s surmountable. Mostly I don&#8217;t know if that&#8217;s &#8220;cool&#8221; screen-reader wise. So if someone know&#8217;s, feel free to chime in. If it&#8217;s <em>not<\/em> cool, we might have to think about using a different element that is cool and relying on JavaScript to invoke the popup.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Basic CSS<\/h2>\n\n\n\n<p>I say basic, because we cannot to tooltip-like styling in CSS for these yet. That is, we cannot position them next to the button that invoked them. We&#8217;ll get that, someday, when we can use the <a href=\"https:\/\/developer.chrome.com\/blog\/tether-elements-to-each-other-with-css-anchor-positioning\">Anchor Positioning API<\/a>. <\/p>\n\n\n\n<p>But we can do everything else that we want in terms of styling. <\/p>\n\n\n\n<p>For one thing, let&#8217;s make the the middle-of-text look of the button look like something you can click. That&#8217;s something Melanie pointed out as a requirement. Rather than just being blue and underlined like a normal link, I&#8217;ll add an icon so it indicates slightly different behavior. We also need to <em>un<\/em>do basic button styling so the button looks more like just some text. I usually have a class for that I call &#8220;text-like&#8221;. So:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">button<\/span><span class=\"hljs-selector-attr\">&#91;popovertarget]<\/span><span class=\"hljs-selector-class\">.text-like<\/span> {\n  <span class=\"hljs-attribute\">border<\/span>: <span class=\"hljs-number\">0<\/span>;\n  <span class=\"hljs-attribute\">background<\/span>: none;\n  <span class=\"hljs-attribute\">font<\/span>: inherit;\n  <span class=\"hljs-attribute\">display<\/span>: inline-block;\n\n  <span class=\"hljs-attribute\">color<\/span>: <span class=\"hljs-number\">#1e88e5<\/span>;\n  <span class=\"hljs-attribute\">text-decoration-style<\/span>: dashed;\n  <span class=\"hljs-attribute\">text-decoration-line<\/span>: underline;\n  <span class=\"hljs-attribute\">text-decoration-color<\/span>: <span class=\"hljs-number\">#42a5f5<\/span>;\n  <span class=\"hljs-attribute\">text-underline-offset<\/span>: <span class=\"hljs-number\">2px<\/span>;\n  <span class=\"hljs-attribute\">overflow<\/span>: visible;\n  \n\n  <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">1.1rem<\/span> <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">0<\/span>; <span class=\"hljs-comment\">\/* space for icon *\/<\/span>\n  <span class=\"hljs-attribute\">background-image<\/span>: <span class=\"hljs-built_in\">url<\/span>(<span class=\"hljs-string\">\"data:image\/svg+xml,%3Csvg xmlns='http:\/\/www.w3.org\/2000\/svg' viewBox='0 0 512 512' width='100' title='question-circle'%3E%3Cpath fill='%231e88e5' d='M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zM262.655 90c-54.497 0-89.255 22.957-116.549 63.758-3.536 5.286-2.353 12.415 2.715 16.258l34.699 26.31c5.205 3.947 12.621 3.008 16.665-2.122 17.864-22.658 30.113-35.797 57.303-35.797 20.429 0 45.698 13.148 45.698 32.958 0 14.976-12.363 22.667-32.534 33.976C247.128 238.528 216 254.941 216 296v4c0 6.627 5.373 12 12 12h56c6.627 0 12-5.373 12-12v-1.333c0-28.462 83.186-29.647 83.186-106.667 0-58.002-60.165-102-116.531-102zM256 338c-25.365 0-46 20.635-46 46 0 25.364 20.635 46 46 46s46-20.636 46-46c0-25.365-20.635-46-46-46z' \/%3E%3C\/svg%3E\"<\/span>);\n  <span class=\"hljs-attribute\">background-size<\/span>: <span class=\"hljs-number\">0.8em<\/span>;\n  <span class=\"hljs-attribute\">background-repeat<\/span>: no-repeat;\n  <span class=\"hljs-attribute\">background-position<\/span>: center right <span class=\"hljs-number\">1px<\/span>;\n\n  &amp;:focus,\n  &amp;:hover {\n    <span class=\"hljs-attribute\">text-decoration-color<\/span>: lightblue;\n    <span class=\"hljs-attribute\">text-decoration-style<\/span>: solid;\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>That leads to a within-paragraph look like this:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"265\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/Screenshot-2024-05-06-at-8.30.13%E2%80%AFAM.png?resize=1024%2C265&#038;ssl=1\" alt=\"\" class=\"wp-image-2058\" style=\"width:523px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/Screenshot-2024-05-06-at-8.30.13%E2%80%AFAM.png?resize=1024%2C265&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/Screenshot-2024-05-06-at-8.30.13%E2%80%AFAM.png?resize=300%2C78&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/Screenshot-2024-05-06-at-8.30.13%E2%80%AFAM.png?resize=768%2C199&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/Screenshot-2024-05-06-at-8.30.13%E2%80%AFAM.png?w=1182&amp;ssl=1 1182w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n<\/div>\n\n\n<p>Then if we apply some super basic styling to the <code>[popover]<\/code> element itself, we can get a popover opening exactly in the middle of the page like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"921\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/Screenshot-2024-05-06-at-8.33.46%E2%80%AFAM.png?resize=1024%2C921&#038;ssl=1\" alt=\"\" class=\"wp-image-2059\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/Screenshot-2024-05-06-at-8.33.46%E2%80%AFAM.png?resize=1024%2C921&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/Screenshot-2024-05-06-at-8.33.46%E2%80%AFAM.png?resize=300%2C270&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/Screenshot-2024-05-06-at-8.33.46%E2%80%AFAM.png?resize=768%2C691&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/Screenshot-2024-05-06-at-8.33.46%E2%80%AFAM.png?w=1418&amp;ssl=1 1418w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<p>A couple of notes here:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>I did <em>not<\/em> use any <code>::backdrop<\/code> styling to style the rest of the page behind the popover. I feel like that may be a bit of an anti-pattern if using for tooltips. I don&#8217;t see any need to mess with the rest of the page when a tooltip is open.<\/li>\n\n\n\n<li>I wanted to use flexbox on the popup, but that caused an issue, because <code>display: flex;<\/code> overrides the default <code>display: none;<\/code> of the popup, making it visible all the time. Instead, you can use <code>[popover]:popover-open { }<\/code> to apply it only when the popover is open. Or use an internal wrapper or whatever.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">The Functional JavaScript<\/h2>\n\n\n\n<p>We need JavaScript here for two jobs:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Normal tooltip behavior suggests you should be able to hover over an element and the tooltip will appear. That cannot be done in HTML alone. Maybe it could be done in CSS somehow with an animation delay or something, but it&#8217;s likely a bit more straightforward in JavaScript.<\/li>\n\n\n\n<li>Again, CSS doesn&#8217;t have anchor positioning yet, so we&#8217;ll do positioning with JavaScript. <\/li>\n<\/ol>\n\n\n\n<p>These are both progressive enhancements, which is nice. So if either thing fails, the tooltip should still work. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The Hover Delay<\/h3>\n\n\n\n<p>For the delay, we&#8217;ll start a timer when the mouse cursor enters a button that opens a popup, if the timer finishes, we&#8217;ll open it. If the mouse cursor leaves before the timeout, we&#8217;ll clear that timeout:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> popoverButtons = <span class=\"hljs-built_in\">document<\/span>.querySelectorAll(\n  <span class=\"hljs-string\">\"button&#91;popovertarget]\"<\/span>\n);\n\npopoverButtons.forEach(<span class=\"hljs-function\">(<span class=\"hljs-params\">button<\/span>) =&gt;<\/span> {\n  <span class=\"hljs-keyword\">let<\/span> timeout = <span class=\"hljs-number\">0<\/span>;\n  button.addEventListener(<span class=\"hljs-string\">\"mouseenter\"<\/span>, () =&gt; {\n    <span class=\"hljs-keyword\">const<\/span> target = button.getAttribute(<span class=\"hljs-string\">\"popovertarget\"<\/span>);\n    <span class=\"hljs-keyword\">const<\/span> popover = <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">\"#\"<\/span> + target);\n    <span class=\"hljs-comment\">\/\/ delay opening<\/span>\n    timeout = setTimeout(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n      popover.showPopover();\n    }, <span class=\"hljs-number\">1500<\/span>);\n  });\n\n  button.addEventListener(<span class=\"hljs-string\">\"mouseleave\"<\/span>, () =&gt; {\n    clearTimeout(timeout);\n  });\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">The Positioning<\/h3>\n\n\n\n<p>I went for a library called <a href=\"https:\/\/floating-ui.com\/\">Floating UI<\/a> to do this. I came across it the other day while using a library called <a href=\"https:\/\/shepherdjs.dev\/\">Shepherd JS<\/a> for creating tours (<a href=\"https:\/\/codepen.io\/pen?welcome=true\">example<\/a>). Shepherd uses it for positioning the steps of the tour, which are usually not far away from what an HTML tooltip might look like. <\/p>\n\n\n\n<p>The <code>.showPopover()<\/code> API is what opens the popup, so the trick is positioning it as it is being opened.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table\"><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>popoverButtons.forEach(<span class=\"hljs-function\">(<span class=\"hljs-params\">button<\/span>) =&gt;<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">let<\/span> timeout = <span class=\"hljs-number\">0<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  button.addEventListener(<span class=\"hljs-string\">\"mouseenter\"<\/span>, () =&gt; {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> target = button.getAttribute(<span class=\"hljs-string\">\"popovertarget\"<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> popover = <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">\"#\"<\/span> + target);\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-comment\">\/\/ delay opening<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    timeout = setTimeout(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>      popover.showPopover();\n<\/span><\/span><mark class='shcb-loc'><span>      computePosition(button, popover, {\n<\/span><\/mark><mark class='shcb-loc'><span>        <span class=\"hljs-attr\">placement<\/span>: <span class=\"hljs-string\">\"top\"<\/span>,\n<\/span><\/mark><mark class='shcb-loc'><span>        <span class=\"hljs-attr\">middleware<\/span>: &#91;flip(), shift({ <span class=\"hljs-attr\">padding<\/span>: <span class=\"hljs-number\">5<\/span> }), offset(<span class=\"hljs-number\">6<\/span>)]\n<\/span><\/mark><mark class='shcb-loc'><span>      }).then(<span class=\"hljs-function\">(<span class=\"hljs-params\">{ x, y }<\/span>) =&gt;<\/span> {\n<\/span><\/mark><mark class='shcb-loc'><span>        <span class=\"hljs-built_in\">Object<\/span>.assign(popover.style, {\n<\/span><\/mark><mark class='shcb-loc'><span>          <span class=\"hljs-attr\">left<\/span>: <span class=\"hljs-string\">`<span class=\"hljs-subst\">${x}<\/span>px`<\/span>,\n<\/span><\/mark><mark class='shcb-loc'><span>          <span class=\"hljs-attr\">top<\/span>: <span class=\"hljs-string\">`<span class=\"hljs-subst\">${y}<\/span>px`<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>        });\n<\/span><\/mark><mark class='shcb-loc'><span>      });\n<\/span><\/mark><span class='shcb-loc'><span>    }, <span class=\"hljs-number\">1500<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>  });\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  button.addEventListener(<span class=\"hljs-string\">\"mouseleave\"<\/span>, () =&gt; {\n<\/span><\/span><span class='shcb-loc'><span>    clearTimeout(timeout);\n<\/span><\/span><span class='shcb-loc'><span>    button.removeAttribute(<span class=\"hljs-string\">\"style\"<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>  });\n<\/span><\/span><span class='shcb-loc'><span>});\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>All that <code>computePosition<\/code> stuff with the <code>middleware<\/code> is just how Floating UI works. The <code>flip()<\/code> function is especially nice, which is essentially &#8220;edge detection&#8221;. Now the popover will attempt to be positioned on top like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"858\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/Screenshot-2024-05-06-at-8.47.28%E2%80%AFAM.png?resize=1024%2C858&#038;ssl=1\" alt=\"\" class=\"wp-image-2060\" style=\"width:575px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/Screenshot-2024-05-06-at-8.47.28%E2%80%AFAM.png?resize=1024%2C858&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/Screenshot-2024-05-06-at-8.47.28%E2%80%AFAM.png?resize=300%2C252&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/Screenshot-2024-05-06-at-8.47.28%E2%80%AFAM.png?resize=768%2C644&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/Screenshot-2024-05-06-at-8.47.28%E2%80%AFAM.png?w=1262&amp;ssl=1 1262w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<p>But if there is no room on top, because that&#8217;s where the edge of the browser window is, it will &#8220;flip&#8221; to the bottom like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"937\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/Screenshot-2024-05-06-at-8.48.54%E2%80%AFAM.png?resize=1024%2C937&#038;ssl=1\" alt=\"\" class=\"wp-image-2061\" style=\"width:560px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/Screenshot-2024-05-06-at-8.48.54%E2%80%AFAM.png?resize=1024%2C937&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/Screenshot-2024-05-06-at-8.48.54%E2%80%AFAM.png?resize=300%2C275&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/Screenshot-2024-05-06-at-8.48.54%E2%80%AFAM.png?resize=768%2C703&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/05\/Screenshot-2024-05-06-at-8.48.54%E2%80%AFAM.png?w=1296&amp;ssl=1 1296w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<p>Nice.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Demo<\/h2>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_rNbXNeg\" src=\"\/\/codepen.io\/anon\/embed\/rNbXNeg?height=1000&amp;theme-id=47434&amp;slug-hash=rNbXNeg&amp;default-tab=html,result\" height=\"1000\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed rNbXNeg\" title=\"CodePen Embed rNbXNeg\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Review<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u2705 The links that open popups looks like that&#8217;s probably what they will do.<\/li>\n\n\n\n<li>\u2705 The links the trigger the popups work. They can be clicked to open (and close) the popup. JavaScript provides a hover-to-open effect as well, mimicking <code>title<\/code> behavior.<\/li>\n\n\n\n<li>\ud83e\udd37\u200d\u2640\ufe0f Unsure if a <code>&lt;button&gt;<\/code> in the middle of a <code>&lt;p&gt;<\/code> where the text is part of the sentence is acceptable from an accessibility perspective.<\/li>\n\n\n\n<li>\ud83e\udd37\u200d\u2640\ufe0f Unsure where the perfect DOM Positioning of the popup would be. My gut tells me to treat it like a footnote, putting them at the end of the main content they apply to. This seems like it would matter quite a bit for something like syndication\/RSS where they might just appear as additional paragraphs without context. Or possibly right after the text element that has the popup so they are closer contextually.<\/li>\n\n\n\n<li>\ud83e\udd37\u200d\u2640\ufe0f I would probably always use <code>popover=\"hint\"<\/code> to allow for multiple popovers to be open at once in a tooltip scenario, but it&#8217;s unclear if that will happen or if that will be the naming.<\/li>\n<\/ul>\n\n\n<div class=\"box article-series\">\n  <header>\n    <h3 class=\"article-series-header\">Article Series<\/h3>\n  <\/header>\n  <div class=\"box-content\">\n            <ol>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/using-the-popover-api-for-html-tooltips\/\">Using the Popover API for HTML Tooltips<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/footnotes-progressively-enhanced-to-popovers\/\">Footnotes Progressively Enhanced to Popovers<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/popovers-work-pretty-nicely-as-slide-out-drawers\/\">Popovers Work Pretty Nicely as Slide-Out Drawers<\/a>\n            <\/li>\n                  <\/ol>\n        <\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>We can *mostly* use HTML alone for this API. But here, we&#8217;ll use CSS to style the &#8220;links&#8221; within paragraphs and a JS library to position them, in lieu of CSS anchoring.<\/p>\n","protected":false},"author":1,"featured_media":1975,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[7,170,31,3,120],"class_list":["post-2027","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-css","tag-floating-ui","tag-html","tag-javascript","tag-popover"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/popup-thumb.jpg?fit=1000%2C500&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/2027","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=2027"}],"version-history":[{"count":10,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/2027\/revisions"}],"predecessor-version":[{"id":2826,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/2027\/revisions\/2826"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/1975"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=2027"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=2027"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=2027"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}