{"id":9712,"date":"2026-05-27T10:14:53","date_gmt":"2026-05-27T15:14:53","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=9712"},"modified":"2026-05-27T10:14:54","modified_gmt":"2026-05-27T15:14:54","slug":"managing-anchor-associations-with-data-attributes-and-advanced-attr","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/managing-anchor-associations-with-data-attributes-and-advanced-attr\/","title":{"rendered":"Managing Anchor Associations With Data Attributes and Advanced attr()"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">When Chrome Canary implemented the <code>anchor<\/code> attribute, which enabled us to establish anchor relationships using HTML instead of CSS, I thought that it was an awesome idea. However, it seems that it won\u2019t be standardized, and that Chrome has removed it along with Firefox, which, it appears, was working on it. It sounds like <a href=\"https:\/\/github.com\/whatwg\/html\/pull\/9144#issuecomment-3893938838\">the reason is that invokers are implicitly anchored already<\/a>, <em>but<\/em>, what about other elements?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this article, I\u2019ll show you an alternative method that leverages the new, more advanced syntax for the <code>attr()<\/code> CSS function.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>But first\u2026<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How the <code>anchor<\/code> attribute <em>would\u2019ve<\/em> worked<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of anchoring an element with <code>position-anchor<\/code> to another element with <code>anchor-name<\/code> and both of them having the same value, we\u2019d anchor an element with the <code>anchor<\/code> attribute to another element with the <code>id<\/code> attribute (and also the same value).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Like this:<\/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-comment\">&lt;!-- Anchor this... (not real, anymore) --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">anchor<\/span>=<span class=\"hljs-string\">\"anchorA\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n<span class=\"hljs-comment\">&lt;!-- ...to this --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"anchorA\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/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 class=\"wp-block-paragraph\">Instead of this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-comment\">\/* Anchor this... (now real) *\/<\/span>\n<span class=\"hljs-selector-id\">#boatA<\/span> {\n  <span class=\"hljs-attribute\">position-anchor<\/span>: --anchorA;\n}\n\n<span class=\"hljs-comment\">\/* ...to this *\/<\/span>\n<span class=\"hljs-selector-id\">#anchorA<\/span> {\n  <span class=\"hljs-attribute\">anchor-name<\/span>: --anchorA;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><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 class=\"wp-block-paragraph\">It wouldn\u2019t have been a better solution (more like an alternative for those that wanted it), but semantic HTML has always been about how elements relate to other elements, so the <code>anchor<\/code> attribute made a lot of sense to me. Think: <code>popovertarget<\/code> and <code>id<\/code> (for popovers), <code>commandfor<\/code> and <code>id<\/code> (for the new invoker commands), <code>interestfor<\/code> and <code>id<\/code> (for the future interest invokers), or even the classic <code>for<\/code> and <code>id<\/code> (for labels). Without the <code>anchor<\/code> attribute, anchoring a bunch of boats to a bunch of anchors looks a bit verbose, like this:<\/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-id\">#boatA<\/span> {\n  <span class=\"hljs-attribute\">position-anchor<\/span>: --anchorA;\n}\n\n<span class=\"hljs-selector-id\">#anchorA<\/span> {\n  <span class=\"hljs-attribute\">anchor-name<\/span>: --anchorA;\n}\n\n<span class=\"hljs-selector-id\">#boatB<\/span> {\n  <span class=\"hljs-attribute\">position-anchor<\/span>: --anchorB;\n}\n\n<span class=\"hljs-selector-id\">#anchorB<\/span> {\n  <span class=\"hljs-attribute\">anchor-name<\/span>: --anchorB;\n}\n\n<span class=\"hljs-selector-id\">#boatC<\/span> {\n  <span class=\"hljs-attribute\">position-anchor<\/span>: --anchorC;\n}\n\n<span class=\"hljs-selector-id\">#anchorC<\/span> {\n  <span class=\"hljs-attribute\">anchor-name<\/span>: --anchorC;\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 class=\"wp-block-paragraph\">To be clear, I mean that every boat looks the same and every anchor looks the same, but each boat needs to be anchored to a specific anchor (think: some kind of reusable, decorative motif that can be anchored to anything). I know, I know, anchors can be <a href=\"https:\/\/frontendmasters.com\/blog\/anchored-menus-and-a-lesson-in-scoping\/\">scoped using <code>anchor-scope<\/code><\/a>, but that\u2019d only work in certain circumstances.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>anchor<\/code> attribute wouldn\u2019t have been a magic bullet, because we still would\u2019ve needed to anchor each <code>anchor<\/code> to its corresponding <code>id<\/code>, but it would\u2019ve been shorter and cleaner. My advanced <code>attr()<\/code> idea isn\u2019t as elegant as the <code>anchor<\/code> attribute would\u2019ve been, but that ship has sailed (<em>ba dum tish!<\/em>), so let\u2019s move on.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setting up anchors using advanced <code>attr()<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">My advanced <code>attr()<\/code> idea combines both concepts. I would\u2019ve moaned about the <code>anchor<\/code> attribute regardless, but knowing how it works <em>is<\/em> important.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First, instead of using the <code>anchor<\/code> and <code>id<\/code> attributes, use data attributes. In addition, make sure that the matching values are formatted as dashed idents (e.g., <code>--anchorA<\/code>), since that\u2019s what <code>position-anchor<\/code> and <code>anchor-name<\/code> accepts.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" 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\">data-boat<\/span>=<span class=\"hljs-string\">\"--anchorA\"<\/span>&gt;<\/span>Boat A<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">data-anchor<\/span>=<span class=\"hljs-string\">\"--anchorA\"<\/span>&gt;<\/span>Anchor A<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">data-boat<\/span>=<span class=\"hljs-string\">\"--anchorB\"<\/span>&gt;<\/span>Boat B<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">data-anchor<\/span>=<span class=\"hljs-string\">\"--anchorB\"<\/span>&gt;<\/span>Anchor B<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">data-boat<\/span>=<span class=\"hljs-string\">\"--anchorC\"<\/span>&gt;<\/span>Boat C<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">data-anchor<\/span>=<span class=\"hljs-string\">\"--anchorC\"<\/span>&gt;<\/span>Anchor C<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><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 class=\"wp-block-paragraph\">That\u2019s right, we\u2019ll still be using <code>position-anchor<\/code> and <code>anchor-name<\/code>, but only once, eliminating any verbosity. In the CSS below, the <code>position<\/code> property is basically the prerequisite for <code>position-anchor<\/code>, whereas the <code>inset<\/code> declaration defines where the boats are positioned relative to their anchor. Anchor positioning stuff, you know?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now for the magic.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of choosing a specific value for <code>position-anchor<\/code>, we use the <code>attr()<\/code> function to copy over the value from the <code>data-boat<\/code> attribute. The <code>position-anchor<\/code> syntax demands a dashed ident, which is cool because that\u2019s what we\u2019ve used (<code>--anchorA<\/code>, <code>--anchorB<\/code>, <code>--anchorC<\/code>, and so on). Now every element with the <code>data-boat<\/code> attribute has <code>position-anchor<\/code> set to a unique value, even though we\u2019ve only written one CSS rule.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We\u2019ve had the CSS delegate the logic to the HTML.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">However, by default, <code>attr()<\/code> values are <code>&lt;string><\/code>s, so the declaration resolves to something like this: <code>position-anchor: \"--anchorA\"<\/code>. That\u2019s why we use the <code>type()<\/code> function to specify the value\u2019s data type, like this: <code>position-anchor: attr(data-boat type(&lt;custom-ident>))<\/code> (<code>&lt;custom-ident><\/code> is the appropriate data type for dashed idents). The reason for this is that the <code>attr()<\/code> function can now be used with any CSS property, whereas it could only be used with the <code>content<\/code> property before, which only accepted <code>&lt;string><\/code>s. And that, by the way, is one of the things that makes <a href=\"https:\/\/una.im\/advanced-attr\">advanced <code>attr()<\/code><\/a> more advanced.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Advanced <code>attr()<\/code> is supported by Chrome, and Firefox 152 will ship it on June 16, 2026. Safari Technology Preview supports it too, but I don\u2019t know when <em>stable<\/em> Safari will support it. My estimation is Safari 27 in September 2026, but it could drop at any moment to be honest, making it \u201cBaseline: Newly Available.\u201d<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After that, we simply do the same thing with <code>anchor-name<\/code> (<code>anchor-name: attr(data-anchor type(&lt;custom-ident&gt;))<\/code>) so that we can link the boats to the anchors. Here\u2019s the full CSS code:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-attr\">&#91;data-boat]<\/span> {\n  <span class=\"hljs-attribute\">position<\/span>: fixed;\n  <span class=\"hljs-attribute\">inset<\/span>: <span class=\"hljs-built_in\">anchor<\/span>(top) auto auto <span class=\"hljs-built_in\">anchor<\/span>(right);\n  <span class=\"hljs-attribute\">position-anchor<\/span>: <span class=\"hljs-built_in\">attr<\/span>(data-boat type(&lt;custom-ident&gt;));\n}\n\n<span class=\"hljs-selector-attr\">&#91;data-anchor]<\/span> {\n  <span class=\"hljs-attribute\">anchor-name<\/span>: <span class=\"hljs-built_in\">attr<\/span>(data-anchor type(&lt;custom-ident&gt;));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><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<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_yyVYGyr\" src=\"\/\/codepen.io\/anon\/embed\/yyVYGyr?height=450&amp;theme-id=1&amp;slug-hash=yyVYGyr&amp;default-tab=html,result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed yyVYGyr\" title=\"CodePen Embed yyVYGyr\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Using advanced <code>attr()<\/code> for other types of associations<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">We can totally use advanced <code>attr()<\/code> to manage other types of associations, but I found it most useful for anchor associations (as above) and maybe animation timelines, like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" 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\">data-scroller<\/span>=<span class=\"hljs-string\">\"--animationA\"<\/span>&gt;<\/span>Scroller A<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">data-animation<\/span>=<span class=\"hljs-string\">\"--animationA\"<\/span>&gt;<\/span>Animation A<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">data-scroller<\/span>=<span class=\"hljs-string\">\"--animationB\"<\/span>&gt;<\/span>Scroller B<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">data-animation<\/span>=<span class=\"hljs-string\">\"--animationB\"<\/span>&gt;<\/span>Animation B<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">data-scroller<\/span>=<span class=\"hljs-string\">\"--animationC\"<\/span>&gt;<\/span>Scroller C<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">data-animation<\/span>=<span class=\"hljs-string\">\"--animationC\"<\/span>&gt;<\/span>Animation C<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><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<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-comment\">\/* Associate each scroller... *\/<\/span>\n<span class=\"hljs-selector-attr\">&#91;data-scroller]<\/span> {\n  <span class=\"hljs-attribute\">scroll-timeline-name<\/span>: <span class=\"hljs-built_in\">attr<\/span>(data-scroller type(&lt;custom-ident&gt;));\n}\n\n<span class=\"hljs-comment\">\/* ...with its linked animation timeline *\/<\/span>\n<span class=\"hljs-selector-attr\">&#91;data-animation]<\/span> {\n  <span class=\"hljs-attribute\">animation-timeline<\/span>: <span class=\"hljs-built_in\">attr<\/span>(data-animation type(&lt;custom-ident&gt;));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><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 class=\"wp-block-paragraph\">But as more CSS features leverage dashed idents (and custom idents overall), this advanced <code>attr()<\/code> trick could turn out to be useful in more ways later on.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><code>ident()<\/code> could make the code cleaner, too<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I came across the <a href=\"https:\/\/drafts.csswg.org\/css-values-5\/#ident\"><code>ident()<\/code> CSS function<\/a> in CSS Values and Units Module Level 5. It\u2019s not supported by any web browser yet, but it sounds like it would make working with custom idents even easier:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-comment\">&lt;!-- Use a simple string value --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">data-something<\/span>=<span class=\"hljs-string\">\"something\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><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<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-attr\">&#91;data-something]<\/span> {\n  <span class=\"hljs-comment\">\/* Convert the string to a custom ident *\/<\/span>\n  <span class=\"hljs-attribute\">property<\/span>: <span class=\"hljs-built_in\">ident<\/span>(attr(data-something));\n\n  <span class=\"hljs-comment\">\/* Convert the string to a dashed ident *\/<\/span>\n  <span class=\"hljs-attribute\">property<\/span>: <span class=\"hljs-built_in\">ident<\/span>(<span class=\"hljs-string\">\"--\"<\/span> attr(data-something));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><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 class=\"wp-block-paragraph\">I think that looks cleaner?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here\u2019s what our anchor association code would look like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" 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\">data-boat<\/span>=<span class=\"hljs-string\">\"anchorA\"<\/span>&gt;<\/span>Boat A<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">data-anchor<\/span>=<span class=\"hljs-string\">\"anchorA\"<\/span>&gt;<\/span>Anchor A<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">data-boat<\/span>=<span class=\"hljs-string\">\"anchorB\"<\/span>&gt;<\/span>Boat B<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">data-anchor<\/span>=<span class=\"hljs-string\">\"anchorB\"<\/span>&gt;<\/span>Anchor B<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">data-boat<\/span>=<span class=\"hljs-string\">\"anchorC\"<\/span>&gt;<\/span>Boat C<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">data-anchor<\/span>=<span class=\"hljs-string\">\"anchorC\"<\/span>&gt;<\/span>Anchor C<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><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<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-attr\">&#91;data-boat]<\/span> {\n  <span class=\"hljs-attribute\">position<\/span>: fixed;\n  <span class=\"hljs-attribute\">inset<\/span>: <span class=\"hljs-built_in\">anchor<\/span>(top) auto auto <span class=\"hljs-built_in\">anchor<\/span>(right);\n  <span class=\"hljs-attribute\">position-anchor<\/span>: <span class=\"hljs-built_in\">ident<\/span>(<span class=\"hljs-string\">\"--\"<\/span> attr(data-boat));\n}\n\n<span class=\"hljs-selector-attr\">&#91;data-anchor]<\/span> {\n  <span class=\"hljs-attribute\">anchor-name<\/span>: <span class=\"hljs-built_in\">ident<\/span>(<span class=\"hljs-string\">\"--\"<\/span> attr(data-anchor));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><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<h2 class=\"wp-block-heading\">Further reading<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/una.im\/advanced-attr\">Advanced <code>attr()<\/code><\/a> by Una Kravets<\/li>\n\n\n\n<li><a href=\"https:\/\/blog.kizu.dev\/dashed-idents-for-everything\/\">Dashed idents<\/a> by Roma Komarov<\/li>\n\n\n\n<li><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Reference\/Global_attributes\/anchor\"><code>anchor<\/code> attribute<\/a> (MDN)<\/li>\n\n\n\n<li><code><a href=\"https:\/\/drafts.csswg.org\/css-values-5\/#ident\">ident()<\/a><\/code> (spec)<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>There is no `anchor` attribute in HTML, it was decided CSS `anchor-name` \/ `position-anchor` was the way to go. But modern CSS functions can get us there anyway.<\/p>\n","protected":false},"author":37,"featured_media":9721,"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":[121,7,483],"class_list":["post-9712","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-anchor","tag-css","tag-ident"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/anchors.jpg?fit=2000%2C1200&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9712","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\/37"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=9712"}],"version-history":[{"count":6,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9712\/revisions"}],"predecessor-version":[{"id":9738,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9712\/revisions\/9738"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/9721"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=9712"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=9712"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=9712"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}