CSS is constantly evolving, and anchor positioning is one of the more recent additions. Rather than having a relative parent containing an absolute child, then using transforms or insets to position it, we can now attach the child to the parent, all in CSS. This opens up a lot of flexibility for things like tooltips, dropdowns, and dynamic UI patterns, without needing as much structural setup or JavaScript.
It’s still early and not widely supported across all browsers yet, but it’s a great example of where CSS is heading — and worth getting familiar with now. This isn’t an in-depth guide on how anchor positioning works, but rather a simple example of what you can start doing with it.
Outline
I want to create a nav bar which is centered on the page and contains links. I want one underline to sit underneath the current link (initially the "Home" link). If I hover over another link I want that underline to move underneath that link.
Firstly lets set up the initial HTML and CSS:
<nav class="nav">
<div class="nav-links">
<a href="#" class="nav-link">Home</a>
<a href="#" class="nav-link">About</a>
<a href="#" class="nav-link">Work</a>
<a href="#" class="nav-link">Contact</a>
</div>
</nav>
and the CSS:
<style>
.nav {
display: flex;
justify-content: center;
padding: 2rem 1rem;
}
.nav_links_wrapper {
display: flex;
}
.nav_links {
text-decoration: none;
color: #111;
font-size: 1rem;
padding-bottom: 0.5rem;
}
</style>
A very basic setup for a nav, but it will serve to illustrate how anchor positioning well.
Setting up Anchor Positioning
Now we need to set up the anchor positioning, like we said before it allows us to attach an element, the "target", to another element, the "anchor". So in our case, the target element is going to be the underline (.nav_links::after) and our initial anchor element will be the current .nav_ links (for this example we will use :first-child in place of the current link).
The way we set this up is the following:
- We need to give the anchor the property
anchor-name: <dashed-ident>#which is a custom name starting with--, like--link-anchor. - Then we give the target, the element we want to anchor the property
position-anchor: <anchor-element>, which would be the custom name we gave to the anchor.
So to translate that into out code and create our underline:
<style>
.nav_links:first-child{
anchor-name: --current-link;
}
.anchor_links::after{
content: '';
position: absolute;
height: 2px;
background: mediumturquoise;
position-anchor: --current-link;
}
</style>
Right now you would see nothing, which makes sense since our ::after is absolute but its "wrapper" (.nav_links) is static. Remember we don't need to set our anchor to relative, what we need to do now is position our underline relative to the anchor.
We do that by setting the left:, right:, top: and bottom: of our after element, but what value do we give to them? We use the anchor() value, which basically says: "Take this value from the anchored element's box" so:
- anchor(left) = uses the left edge of the anchored element.
- anchor(right) = uses the right edge of the anchored element.
- anchor(bottom) uses the bottom edge of the anchored element.
So the code for our ::after element becomes:
<style>
.anchor_links::after{
content: '';
position: absolute;
height: 2px;
background: mediumturquoise;
position-anchor: --current-link;
left: anchor(left);
right: anchor(right);
bottom: anchor(bottom);
}
</style>
So we essentially restrict the width of the ::after to be as wide as the anchor (.anchor_links) and we use anchor(bottom) to place the ::after the bottom of the anchor. Notice also how we only see the one ::after element, not 4 (one for each link).
Animating the Underline
Now we have the initial position of the underline setup we need to get it to move underneath the link we are hovering over. To achieve this what we can do is change which element has the anchor-name. If we give the anchor-name to the the ::after of the link we are hovering over like so:
<style>
.anchor_links:hover {
anchor-name: --current-link;
}
</style>
Now when you hover the left and right positions of the ::after will update to match the new anchor. This causes the underline to move under the now hovered link! The best part is because we are changing the left and right values this is now animatable, so the full code for the anchor and target becomes:
<style>
.anchor_links:first-child{
anchor-name: --current-link;
}
.anchor_links::after{
content: '';
position: absolute;
height: 2px;
background: mediumturquoise;
position-anchor: --current-link;
left: anchor(left);
right: anchor(right);
bottom: anchor(bottom);
transition: left 500ms, right 500ms;
}
.anchor_links:hover{
anchor-name: --nav-wrap;
}
</style>
Notice that we have no gaps between link elements. If we added a gap and our mouse moved over it, the underline would move back under the :first-child as we are no longer hovering over a link. To avoid this we wrap each link in a div and give that div padding left and right. The HTML will become:
<style>
<nav class="nav">
<div class="nav_links_wrapper">
<div class="nav_link_wrap">
<a href="#" class="nav_link">Home</a>
</div>
<div class="nav_link_wrap">
<a href="#" class="nav_link">Home</a>
</div>
<div class="nav_link_wrap">
<a href="#" class="nav_link">Home</a>
</div>
<div class="nav_link_wrap">
<a href="#" class="nav_link">Home</a>
</div>
</div>
</nav>
</style>
and the CSS:
<style>
.nav_links_wrap:first-child .anchor_links{
anchor-name: --current-link;
}
.nav_links_wrap{
padding-left: 1rem;
padding-right: 1rem;
}
.anchor_links::after{
content: '';
position: absolute;
height: 2px;
background: mediumturquoise;
position-anchor: --current-link;
left: anchor(left);
right: anchor(right);
bottom: anchor(bottom);
transition: left 500ms, right 500ms;
}
.nav_links_wrap:hover .anchor_links{
anchor-name: --nav-wrap;
}
</style>
Now we have a "gap" between links which prevents the underline from shooting back to the current link.
While this is not a guide or a complete how-to, I do hope this peaked your interest in anchor positioning. When full browser support is reached, this will be a powerful asset to be able to level up your Webflow builds.