One Of Those “Onboarding” UIs, With Anchor Positioning


, semantically enhanced to become a tooltip with the [role="tooltip"] attribute. We can also use [role="tooltip"] as a semantic selector to add common styles to tooltips, including the tooltip’s positioning relative to its anchor.

First, let’s turn our button into an anchored element by giving it an anchor-name. Next, we can set the target element’s position-anchor to match the anchor-name of the anchored element. By default, we can set the tooltip’s visibility to hidden, then using CSS sibling selectors, if the target element receives hover or focus-visible, we can then swap the visibility to visible.

/* Anchor-positioned Element */
#inbox-tool {
  anchor-name: --inbox-tool;
}

/* Target element */
[role="tooltip"]#inbox-label {
  position-anchor: --inbox-tool
}

/* Target positioning */
[role="tooltip"] {
  position: absolute;
  position-area: end center;
  /* Hidden by default */
  visibility: hidden;
}

/* Visible when tool is hovered or receives focus */
.tool:hover + [role="tooltip"],
.tool:focus-visible + [role="tooltip"] {
  visibility: visible;
}

Ta-da! Here we have a working, CSS anchor-positioned tooltip!

As users of touch devices aren’t able to hover over elements, you may want to explore toggletips instead!

Floating disclosures

Disclosures are another common component pattern that might be a perfect use case for anchor positioning. Disclosures are typically a component where interacting with a toggle will open and close a corresponding element. Think of the good ol’ /

HTML element duo, for example.

Currently, if you are looking to create a disclosure-like component which floats over other portions of your user interface, you might be in for some JavaScript, absolute positioning, and z-index related troubles. Soon enough though, we’ll be able to combine CSS anchor positioning with another newer platform feature [popover] to create some incredibly straightforward (and semantically accurate) floating disclosure elements.

The Popover API provides a non-modal way to elevate elements to the top-layer, while also baking in some great functionality, such as light dismissals.

Zell also has more information on popovers, dialogs, and modality!

One of the more common patterns you might consider as a “floating disclosure”-type component is a dropdown menu. Here is the HTML we’ll work with:

We can set our target element, in this case the , to be our popover element by adding the [popover] attribute.

To control the popover, let’s add the attribute [popoveraction="toggle"] to enable the button as a toggle, and point the [popovertarget] attribute to the [id] of our target element.

No JavaScript is necessary, and now we have a toggle-able [popover] disclosure element! The problem is that it’s still not tethered to the anchor-positioned element, let’s fix that in our CSS.

First, as this is a popover, let’s add a small bit of styling to remove the intrinsic margin popovers receive by default from browsers.

ul[popover] {
  margin: 0;
}

Let’s turn our button into an anchor-positioned element by providing it with an anchor-name:

ul[popover] {
  margin: 0;
}

#anchor {
  anchor-name: --toggle;
}

As for our target element, we can attach it to the anchor-positioned element by setting its position to absolute and the position-anchor to our anchor-positioned element’s anchor-name:

ul[popover] {
  margin: 0;
}

#anchor {
  anchor-name: --toggle;
}

#target {
  position: absolute;
  position-anchor: --toggle;
}

We can also adjust the target’s positioning near the anchor-positioned element with the position-area property, similar to what we did with our tooltip.

ul[popover] {
  margin: 0;
}

#anchor {
  anchor-name: --toggle;
}

#target {
  position: absolute;
  position-anchor: --toggle;
  position-area: bottom;
  width: anchor-size(width);
}

You may notice another CSS anchor function in here, anchor-size()! We can set the target’s width to match the width of the anchor-positioned element by using anchor-size(width).

There is one more neat thing we can apply here, fallback positioning! Let’s consider that maybe this dropdown menu might sometimes be located at the bottom of the viewport, either from scrolling or some other reason. We don’t really want it to overflow or cause any extra scrolling, but instead, swap to an alternate location that is visible to the user.

Anchor positioning makes this possible with the postion-try-fallbacks property, a way to provide an alternate location for the target element to display near an anchor-positioned element.

#target {
  position: absolute;
  position-anchor: --toggle;
  position-area: bottom;
  postion-try-fallbacks: top;
  width: anchor-size(width);
}

To keep things simple for our demo, we can add the opposite value of the value of the postion-area property: top.

Shopping cart component

We know how to make a tooltip and a disclosure element, now let’s build upon what we’ve learned so far and create a neat, interactive shopping cart component.

Let’s think about how we want to mark this up. First, we’ll need a button with a shopping cart icon:

We can already reuse what we learned with our tooltip styles to provide a functioning label for this toggle. Let’s add the class .tool to the button, then include a tooltip as our label.





We’ll need to specify our

To control the popover, we’ve added [popovertarget="shopping-cart"] and [popoveraction="toggle"] to our anchor-positioned element and included a second button within the

that can also be used to close the dialog with [popoveraction="close"].

To anchor the shopping cart

to the toggle, we can set position-anchor and position-area:

#shopping-cart {
  position-anchor: --shopping-cart;
  position-area: end center;
}

At this point, we should take a moment to realize that we have tethered two elements to the same anchor!

We won’t stop there, though. There is one more enhancement we can make to really show how helpful anchor positioning can be: Let’s add a notification badge to the element to describe how many items are inside the cart.

Let’s place the badge inside of our anchor-positioned element this time.



We can improve our tooltip to include verbiage about how many items are in the cart:


Now the accessible name of our anchor-positioned element will be read as Shopping Cart (1 item in cart), which helps provide context to assistive technologies like screen readers.

Let’s tether this notification badge to the same anchor as our tooltip and shopping cart

, we can do this by setting the position-anchor property of the badge to --shopping-cart-toggle:

#shopping-cart-badge {
  position: absolute;
  position-anchor: --shopping-cart-toggle;
}

Let’s look at positioning. We don’t want it below or next to the anchor, we want it overlapping, so we can use CSS anchor functions to position it based on the anchor-positioned element’s dimensions.

#shopping-cart-badge {
  position: absolute;
  position-anchor: --shopping-cart-toggle;
  bottom: anchor(center);
  left: anchor(center);
}

Here we are setting the bottom and left of the target element to match the anchor’s center. This places it in the upper-right corner of the SVG icon!

black shopping cart icon with a red circle badge containing the number 1

Folks, this means we have three elements anchored now. Isn’t that fantastic?

Combining things

To put these anchor-positioned elements into perspective, I’ve combined all the techniques we’ve learned so far into a more familiar setting:

Disclosure components, dropdown menus, tooltips (and toggletips!), as well as notification badges all made much simpler using CSS anchor positioning!

Final project

As a final project for myself (and to bring this whole thing around full-circle), I decided to try to build a CSS anchor-positioned-based onboarding tool. I’ve previously attempted to build a tool like this at work, which I called “HandHoldJS” and it… well, it didn’t go so great. I managed to have a lot of the core functionality working using JavaScript, but it meant keeping track of quite a lot of positions and lots of weird things kept happening!

Let’s see if we can do better with CSS anchor positioning.

Feel free to check out the code on CodePen! I went down quite a rabbit hole on this one, so I’ll provide a bit of a high-level overview here.

is a native custom element that works entirely in the light DOM. It sort of falls into the category of an HTML web component, as it is mostly based on enabling its inner HTML. You can specify tour stops to any element on the page by adding [data-tour-stop] attributes with values in the order you want the tour to occur.

Inside the element contains a



Read more