Frontend Masters Boost RSS Feed https://frontendmasters.com/blog Helping Your Journey to Senior Developer Mon, 10 Nov 2025 19:45:55 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.3 225069128 Perfectly Pointed Tooltips: To The Corners https://frontendmasters.com/blog/perfectly-pointed-tooltips-to-the-corners/ https://frontendmasters.com/blog/perfectly-pointed-tooltips-to-the-corners/#respond Mon, 10 Nov 2025 15:04:33 +0000 https://frontendmasters.com/blog/?p=7714 Ready for the last challenge?

We are still creating tooltips that follow their anchors, and this time we will work with new positions and learn new tricks. I will assume you have read and understood the first two parts, as I will skip the things I already explained there. Fair warning, if you haven’t read those first two you might get a little lost.

Article Series

At the time of writing, only Chrome and Edge have full support of the features we will be using.

As usual, a demo of what we are making:

This time, instead of considering the sides, I am considering the corners. This is another common pattern in the tooltip world. The code structure and the initial configuration remain the same as in the previous examples, so let’s jump straight into the new stuff.

Defining The Positions

If you took the time to explore my interactive demo, you already know the position we will start with:

position-area: top left;

The other positions will logically be top rightbottom left, and bottom right. We already learned that defining explicit positions is not the ideal choice, so let’s flip!

The flipped values are:

position-try-fallbacks: flip-inline, flip-block, flip-block flip-inline; 

The advantage of this configuration is that we are not using flip-start, so we can safely define min-width (or max-height) without issue. The drawback is that adding the tail is complex. It needs to be placed on the corners, and the margin trick won’t work. We need another hack.

Notice how I am using margin instead of inset to control the gap between the tooltip and the anchor. Both are correct, but you will see later why margin is slightly better in my use case.

Adding The Tail

In the previous examples, the logic is to draw a shape with all the tails, then hide the non-needed parts. The tail has the same color as the tooltip and is placed behind its content, so we can only see what is outside the boundary of the tooltip.

This time, I will use a slightly different idea. I am still drawing a shape with all the tails, but the hiding technique will be different.

First, we place the pseudo-element of the tooltip above the anchor. Not on the top of it, but both will overlap each other.

#tooltip::before {
  content: "";
  position: fixed;
  position-anchor: --anchor;
  position-area: center;
  width:  anchor-size(width);
  height: anchor-size(height);
}

I am using a fixed position to be able to “see” the anchor (we talked about this quirk in the first article). Then, I place the element in the center area, which means above the anchor element (or below it depending on the z-index).

I am introducing a new function, anchor-size(), which is part of the Anchor Positioning API. We saw the anchor() function, which allows us to query the position from an anchor element. anchor-size()does the same but with the sizes. I am using it to make the pseudo-element have the same size as the anchor. It’s like using width: 100% where 100% refers to the anchor.

Nothing fancy so far. We have a square behind the anchor.

Let’s increase the size a little so it also touches the tooltip. We add twice the gap defined by the variable --d plus the value of --s, which controls both the radius and the size of the tooltip.

#tooltip {
  --d: .5em; /* distance between anchor and tooltip */
  --s: .8em; /* tail size & border-radius */ 
}

#tooltip:before {
  width:  calc(anchor-size(width) +  2*(var(--d) + var(--s)));
  height: calc(anchor-size(height) + 2*(var(--d) + var(--s)));
}

It seems we are going nowhere with this idea but, believe me, we are almost there.

Now we sculpt the pseudo-element to have the shape of a tail on each corner, like illustrated below:

Illustration showing a blue square transitioning into a tooltip design with four symmetrical tails around a centered anchor icon.

I am using a somewhat verbose clip-path value to create the final shape but the method used is not particularly important. You can consider gradients, SVG background, the new shape() function, etc. Perhaps you would also like to have a different design for the tails. The main idea is to have four tails around the anchor.

Do you start to see the tricks? We have the correct position for the tails (you can drag the anchor and see the result), but we still have to hide the extra ones.

All we need is to add one line of code to the tooltip:

clip-path: inset(0) margin-box;

I know it’s not very intuitive but the explanation is fairly simple. Even if the pseudo-element is using a fixed position and has lost its relation with the tooltip, it remains part of its content, so clipping the tooltip will also affect the pseudo-element.

In our case, the clip-path will consider the margin box as its reference to create a basic rectangle using inset(0) that will show only what is inside it. In other words, anything outside the margin area is hidden.

Toggle the “debug mode” in the demo below and you will see a black rectangle that illustrates the clip-path area.

Only one tail can fit that rectangle, which is perfect for us!

This trick sounds cool! Can’t we apply it to the previous demo as well?

We can! This series of articles could have been one article detailing this trick that I apply to the three examples, but I wanted to explore different ideas and, more importantly, learn about anchor positioning through many examples. Plus, it’s always good to have various methods to achieve the same result.

What about trying to redo the previous example using this technique? Take it as homework to practice what you have learned through this series. You will find my implementation in the next section.

More Examples

Let’s start with the previous demos using the new technique. As usual, you have the debug mode to see what’s going on behind the scenes.

I will conclude with one final example for you to study. You can also try to implement it before checking my code if you want another challenge.

And a version with a curved tail:

Conclusion

I hope you enjoyed this article series. Our goal was to leverage modern CSS to create common tooltip patterns, while also exploring the powerful Anchor Positioning API. It’s one of those modern features that introduce new mechanisms into the CSS world. We are far from the era where we simply define properties and see a static result. Now we can link different elements across the page, create conditional positioning, define a dynamic behavior that adjusts to each situation, and more!

This feature is only at its Level 1. The Level 2 will introduce even more ideas, one of which is the ability to query the fallback positions and apply a custom CSS. Here is one of the previous demos using this future technique:

The code is probably more verbose, but it feels less hacky and more intuitive. I let you imagine all the possibilities you can do with this technique.

Article Series

]]>
https://frontendmasters.com/blog/perfectly-pointed-tooltips-to-the-corners/feed/ 0 7714
Perfectly Pointed Tooltips: All Four Sides https://frontendmasters.com/blog/perfectly-pointed-tooltips-all-four-sides/ https://frontendmasters.com/blog/perfectly-pointed-tooltips-all-four-sides/#comments Mon, 03 Nov 2025 16:15:35 +0000 https://frontendmasters.com/blog/?p=7543 Time for part two! We’ve got really nice functional positioned tooltips already, but they were mostly concerned with “pointing” up or down and shifting at the edges to avoid overflow. Now we’re going to take it further, considering four positions without shifts.

Article Series

At the time of writing, only Chrome and Edge have full support of the features we will be using.

Here is a demo of what we are making:

Drag the anchor and see how the tooltip switches between the four positions and how it remains centered relatively to the anchor.

The Initial Configuration

We are going to use the same code structure as in the first part. We start with the tooltip placed above the anchor (the “top”).

<div id='anchor'></div>
<div id='tooltip'></div>
#anchor {
  anchor-name: --anchor;
}
#tooltip {
  --d: 1em; /* distance between tooltip and anchor */

  position: absolute; 
  position-anchor: --anchor;
  position-area: top;
  bottom: var(--d);
}

From here on, things will be different from the previous example.

Defining Multiple Positions

The position-try-fallbacks property allows us to define multiple positions. Let’s try the following:

position-try-fallbacks: bottom, left, right;

Let’s not forget that the placement is related to the containing block, which is the body in our example (illustrated with the dashed border):

We almost have the same behavior as the first example; however if you are close to the right or left edges, you get the new positions. Instead of overflowing, the browser will swap to the right or left position.

Illustration showing a tooltip following an anchor, with a crossed-out example on the left and a correct behavior on the right, displaying the text 'Drag the anchor and I should follow...'

Similar to the first example, the gap disappears when switching to the fallback positions. We know how to fix it! Instead of explicitly defining the positions, we can rely on the “flip” feature.

To move from top to bottom, we use flip-block:

position-try-fallbacks: flip-block, left, right;

From top to left, we use flip-start:

position-try-fallbacks: flip-block, flip-start, right; 

The flip-block value mirrors the position across the horizontal axis, and flip-start does the same across the diagonal. With this value, we can move from top to left and from bottom to right. And logically, we also have a flip-inline that considers the vertical axis to move from left to right.

But how do we move from top to right? We are missing another value, right?

No, we have all the necessary values. To move from top to right, we combine two flips: flip-block to move to the bottom, then flip-start to move to the right:

position-try-fallbacks: flip-block, flip-start, flip-block flip-start;

Or flip-start to move to the left, and then flip-inline to move to the right:

position-try-fallbacks: flip-block, flip-start, flip-start flip-inline;

It should be noted that all the flips consider the initial position defined on the element and not the previous position defined on position-try-fallbacks or the current position. If we first perform a flip-block to move to the bottom, the flip-start of the second position will not consider the bottom position but the top position (the initial one). This can be confusing, especially when you have many positions.

Said differently, the browser will first transform all the flips into positions (considering the initial position) and then pick the suitable one when needed.

Disabling the Shift Behavior

What we have is actually good and might work perfectly for some use-cases, but we’re aiming for slightly more advanced functionality. What we want is to flip to the left or right position as soon as the tooltip touches the edges. We don’t want to have the “shift” behavior. I want the tooltip to remain always centered relatively to the anchor.

Image showing four tooltip positions in relation to an anchor, with text indicating interaction.

For this, we can use:

justify-self: unsafe anchor-center;

What is this strange value!?

After defining the position of an element using position-area we can also control its alignment using justify-self and align-self (or the shorthand place-self). However, we get a default alignment that you rarely need to change.

For position-area: top, the default alignment is equivalent to justify-self: anchor-center and align-self: end.

Don’t we have a center value? Why is it called anchor-center?

The center value exists, but its behavior is different from anchor-center. The center value considers the center of the area, while anchor-center considers the center of the anchor in the relevant axis.

Here is a screenshot taken from my interactive demo, where you can see the difference:

Comparison of element alignment in CSS, showing the difference between centering in the top area versus centering at the anchor point.

In addition to that, anchor-center follows the logic of safe alignment which cause the shift behavior. When there is not enough room for centering, the element will shift to remain within the containing block area. To disable this, we tell the browser to consider an “unsafe” behavior hence the use of:

justify-self: unsafe anchor-center;

Here is a demo with only the top and bottom positions. Notice how the tooltip will overflow from the left and right sides instead of shifting.

And if we add back the left and right positions to the fallbacks, the browser will use them instead of overflowing!

It should be noted that justify-self is also included in the flip. It’s one of those properties that the browser changes when flipping. When the position is top or bottom, it remains justify-self, but when the position is left or right, it becomes align-self. Another reason why it’s better to consider the flip feature instead of explicitly defining a position.

Adding min-width

The position of the tooltip is now good, but in some particular cases, it’s too narrow.

A tooltip with a blue background displaying the text 'Drag the anchor and I should follow...' is positioned above a gray anchor icon.

That’s a logical behavior since the text inside can wrap to make the tooltip fit that position. You probably want to keep that behavior, but in our case, we’d like to add min-width to force it to flip to another position before shrinking too much. It can also be a max-height as well.

Oops, min-width is not preventing wrapping, but it is increasing the height! What?!

Can you guess what the issue is? Think a moment about it.

It’s the flip behavior.

The min-width and all the sizing properties are also affected by the flip. The initial configuration is top, so defining min-width means that when we perform a flip-start to move to the left or the right position, the min-width becomes min-height, which is not good.

So we define min-height instead, when flipped it becomes min-width!

Yes, but the min-height will apply to the top and bottom positions, which is not ideal either.

We can fix this by using custom positions where we define all the properties manually.

#tooltip {
  min-width: 10em;

  position-area: top;
  justify-self: unsafe anchor-center;
  bottom: var(--d);
  position-try-fallbacks: flip-block,--left,--right;
}
@position-try --left {
  position-area: left;
  justify-self: normal;
  align-self: unsafe anchor-center;
  right: var(--d);
}
@position-try --right {
  position-area: right;
  justify-self: normal;
  align-self: unsafe anchor-center;
  left: var(--d);
}

We use @position-try to create a custom position with a given name, and inside it we define all the properties. Instead of using flip-start to set the left position, I define a custom --left position with all the necessary properties to correctly place the tooltip on the left. Same for the right position. In this situation, min-width is preserved for all positions, as we are no longer using flip-start.

It is worth noting that when using a custom position, you need to ensure that you override all the properties of the initial position defined on the element otherwise they still apply. For this reason, I am defining justify-self: normal to override justify-self: unsafe anchor-centernormal being the default value of justify-self.

While this solution works fine, it’s a bit verbose, so I was wondering if we can do better. It turns out we can!

We can combine the flip feature and custom positions to get a shorter code:

#tooltip {
  position-area: top;
  justify-self: unsafe anchor-center;
  bottom: var(--d);
  position-try: flip-block,--size flip-start,--size flip-start flip-inline;
}
@position-try --size {
  min-height: 12em; /* this is min-width! */
}

When we define a custom position with a flip, the browser selects the properties within the custom position, as well as the properties already defined on the element, and then performs the flip. So --size flip-start will flip the properties defined on the element and the one defined in the custom position --sizemin-height becomes a min-width! Clever, right?

But you said we cannot use min-height?

We cannot use it on the main element as it will apply to the top and bottom positions. However, within a custom position, I can select where it applies, and I want it to apply only to the left and right positions. Plus, I don’t need any min-width or min-height constraint when the position is top or bottom.

Now our tooltip position is perfect! Let’s add the tail.

Adding The Tail

First, we create a shape that contains the 4 tails.

Comparison of tooltip shapes demonstrating the transition from a red diamond shape to a blue rounded shape with the text 'Drag the anchor and I should follow...'
#tooltip:before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: calc(-1*var(--d));
  clip-path: polygon(
    calc(50% - var(--s)) var(--d),50% .2em,calc(50% + var(--s)) var(--d),
    calc(100% - var(--d)) calc(50% - var(--s)), calc(100% - .2em) 50%,calc(100% - var(--d)) calc(50% + var(--s)),
    calc(50% + var(--s)) calc(100% - var(--d)),50% calc(100% - .2em),calc(50% - var(--s)) calc(100% - var(--d)),
    var(--d) calc(50% + var(--s)), .2em 50%,var(--d) calc(50% - var(--s))
  );
}

Then we control it using margin on the tooltip element, just as we did in the first part. When the position is top, we add a margin to all the sides except for the bottom one:

margin: var(--d);
margin-bottom: 0;
Comparison of tooltip designs showing a red diamond-shaped tooltip on the left and a blue rectangular tooltip on the right, both displaying the text 'Drag the anchor and I should follow...'.

And for the other sides, we do nothing! The flip will do the job for us.

Toggle the “debug mode” to see how the shape behaves in each position.

Conclusion

We have completed the second part. Now, you should be comfortable working with fallbacks, the flip feature, and custom positions. If you are still struggling, give the article another read. We still have one final challenge, so make sure everything is clear before moving to the next article.

Article Series

]]>
https://frontendmasters.com/blog/perfectly-pointed-tooltips-all-four-sides/feed/ 2 7543
Perfectly Pointed Tooltips: A Foundation https://frontendmasters.com/blog/perfectly-pointed-tooltips-a-foundation/ https://frontendmasters.com/blog/perfectly-pointed-tooltips-a-foundation/#comments Tue, 28 Oct 2025 16:51:29 +0000 https://frontendmasters.com/blog/?p=7514 Tooltips are a classic in web development. You click on an element, and a small bubble appears to display additional details. Behind that simple click, there tends to be JavaScript performing calculations to determine the correct position for the tooltip. Let’s try to place it at the top. Nope, not enough space. Let’s try the bottom instead. It’s also touching the right edge so let’s shift it a bit to the left. There is a lot that can go into making sure a tooltip is placed well without any cutoffs losing information.

In this article, I will show you how to write good JavaScript that covers all the possibilities…

Kidding! We’ll be using CSS and I will show how the modern anchor positioning API can help us with all this. None of the weight and performance concerns of JavaScript here.

Article Series

At the time of writing, only Chrome and Edge have full support of the features we will be using.

Let’s start with a demo:

Click-and-drag the anchor and see how the tooltip behaves. It will try to position itself in a way to remain visible and avoid any overflow. Cool, right? No JavaScript is used to position the tooltip (except the one for dragging the anchor, which is irrelevant to the trick).

This is made possible thanks to the new Anchor Positioning API and a few other tricks we will dissect together. We will also study more examples, so if you are new to anchor positioning, you are in the right place.

The Initial Configuration

Let’s start with the markup: An anchor element and its tooltip:

<div id='anchor'></div>
<div id='tooltip'></div>

This isn’t interesting HTML, but it does showcase how the anchor and the tooltip are different elements that don’t need to be parent/child. They can be anywhere in the DOM and the CSS can handle that (though, for practical and accessibility reasons, you may want to keep them close together and associate them).

The HTML structure you use will depend on your use case and your type of content, so choose it carefully. In all cases, it’s mainly one element for the anchor and another one for the tooltip.

Here is a demo taken from another article where the anchor is the thumb of a slider and the tooltip is an <output> element:

The CSS:

#anchor {
  anchor-name: --anchor;
}
#tooltip {
  position: absolute; 
  position-anchor: --anchor;
  position-area: top;
}

We define the anchor using anchor-name, link the tooltip to the anchor using position-anchor (and a custom ident, the --anchor bit that looks like a custom property but is really just a unique name), and then we place it at the top using position-area. The tooltip needs to be absolutely positioned (which includes fixed position as well).

Nothing fancy so far. The tooltip is “always” placed at the top, whatever the anchor’s position. You can drag the anchor to see the result.

In this article we’ll use simple values for position-area, but this property can be very tricky.

A grid layout demo showing various cell configurations and an anchor icon with the text 'CSS Is Awesome' positioned at the top left.
I’ve created an interactive demo if you want to explore all the different values and understand how alignment works in the context of Anchor Positioning.

Now that our tooltip is placed, let’s add a small offset at the bottom to prepare the space for the tail. Using bottom will do the job.

#anchor {
  anchor-name: --anchor;
}
#tooltip {
  --d: 1em; /* distance between tooltip and anchor */

  position: absolute; 
  position-anchor: --anchor;
  position-area: top;
  bottom: var(--d);
}

Making the Position Dynamic

Let’s move to the interesting part, where we will adjust the position of the tooltip to make it always visible and avoid any overflow. Anchor Positioning has native mechanisms to do this, and all we need to do is understand how to use them correctly.

The first thing is to identify the containing block of the absolutely positioned element. We may intuitively think that the logic is to avoid a screen overflow, but that’s not the case. It’s related to the containing block. This can be very confusing if you don’t understand this part, so let’s look closely.

The specification:

Anchor positioning, while powerful, can also be unpredictable. The anchor box might be anywhere on the page, so positioning a box in any particular fashion might result in the positioned box overflowing its containing block or being positioned partially off screen.

To ameliorate this, an absolutely positioned box can use the position-try-fallbacks property to refer to several variant sets of positioning/alignment properties that the UA can try if the box overflows its initial position. Each is applied to the box, one by one, and the first that doesn’t cause the box to overflow its containing block is taken as the winner.

As you can read, it’s all about the containing block, and the containing block of an absolutely positioned element is the first ancestor with a position different from static (the default). If such element doesn’t exist we consider the initial containing block.

In our example, I am going to use the body as the containing block, and I will add a border and an offset from each side to better illustrate:

Drag the anchor to the left or the right and see what happens. When the tooltip touches the edges, it stops, even if you can still move the anchor. It overflows the body only when the anchor is getting outside.

The browser will initially place the tooltip at the top and centered. The priority is to remain withing the containing block, so if there isn’t enough space to keep the center behavior, the tooltip is shifted. The second priority is to keep the anchor behavior, and in this case, the browser will allow the overflow if the anchor element is outside.

A three-part interactive demo showing a tooltip following an anchor element as it is dragged. The tooltip displays the message 'Drag the anchor and I should follow...' with an anchor icon below.

Assuming the anchor will remain within the body area, we already have what we want without too much effort. The tooltip will never overflow from the right, left, or bottom side. What remains is the top.

By default, the browser can shift the element within the area defined by position-area, but cannot do more than that. We need to instruct the browser on how to handle the other cases. For this, we use position-try-fallbacks where we define different positions for the browser to “try” in case the element doesn’t fit its containing block.

Let’s define a bottom position:

position-try-fallbacks: bottom;

Drag the anchor to the top and see what happens:

Now, when the tooltip overflows the body from the top, the position becomes “bottom”. It will also remain bottom until the tooltip overflows again from the bottom. In other words, when the browser picks a position after an overflow, it keeps it until a new overflow happens.

That’s all, we are done! Now our tooltip is perfectly placed, whatever the anchor position.

But we no longer have the gap when the position is at the bottom (for the future arrow). How do we fix that?

We told the browser to only change the value of position-area to bottom, but we can do better by using:

position-try-fallbacks: flip-block;

“Block” refers to the block axis (the vertical axis in our default writing mode), and this instruction means flip the position across the vertical axis. The logic is to mirror the initial position on the other side. To do this the browser needs to update different properties in addition to position-area.

In the example we’ve defined position-area: top and bottom: var(--d). With position-try-fallbacks: flip-block in place, when that flip happens, it’s as if we defined position-area: bottom and top: var(--d). We keep the gap!

If you are a bit lost and confused, don’t worry. We are dealing with new mechanisms not common to the CSS world so it may take time to click for you.

To sum up, we can either instruct the browser to update only the position-area by defining a new position or to “flip” the actual position across one axis which will update different properties.

Adding The Tail

Adding a tail to a tooltip is pretty straightforward (I even have a collection of 100 different designs), but changing the direction of the tail based on the position is a bit tricky.

Three tooltip examples illustrating text that says 'Drag the anchor and I should follow...' with an anchor icon, showcasing dynamic positioning.

For now, Anchor Positioning doesn’t offer a way to update the CSS based on the position, but we can still use the existing features to “hack” it. Hacking with CSS can be fun!

I am going to rely on the “flip” feature and the fact that it can update the margin to achieve the final result.

First, I will consider a pseudo-element to create the tail shape:

#tooltip {
  --d: 1em; /* distance between tooltip and anchor */
  --s: 1.2em; /* tail size */
}
#tooltip::before {
  content: "";
  position: absolute;
  z-index: -1;
  width: var(--s);
  background: inherit;
  inset: calc(-1*var(--d)) 0;
  left: calc(50% - var(--s)/2);
  clip-path: polygon(50% .2em,100% var(--d),100% calc(100% - var(--d)),50% calc(100% - .2em),0 calc(100% - var(--d)),0 var(--d));
}

Both tails are visible by default. Click “debug mode” to better understand the shape and how it’s placed.

When the tooltip is at the top, we need to hide the top part. For this, we can use a margin-top on the pseudo-element equal to variable --d. And when the tooltip is at the bottom, we need margin-bottom.

I am going to define the margin on the tooltip element and then inherit it on pseudo-element:

#tooltip {
  --d: 1em; /* distance between tooltip and anchor */
  --s: 1.2em; /* tail size */

  margin-top: var(--d);
}
#tooltip::before {
  margin: inherit;
}

Tada. Our tooltip is now perfect! The use of margin will hide one side keeping one tail visible at a time.

But we didn’t define margin-bottom. How does it work for the bottom position?

That’s the “flip” feature. Remember what we did with the gap where we only defined top and flip-block changed it to bottom? The same logic applies here with margin: the margin-top automatically becomes a margin-bottom when the position is flipped! Cool, right?

Note that using margin will cause the tooltip to flip a bit earlier since margin is part of the element, and the logic is to prevent the overflow of the “margin box”. It’s not a big deal in our example; it’s nicer to flip the position before it touches the edges.

Moving The Tail

The top and bottom parts are good, but we still need to fix the cases where the tooltip shifts when it’s close to the left and right edges. The tail needs to follow the anchor. For this, we have to update the left value and make it follow the anchor position.

Instead of:

left: calc(50% - var(--s));

We use:

left: calc(anchor(--anchor center) - var(--s)/2); 

I replace 50%, which refers to the center of the tooltip element, with anchor(--anchor center), which is the center of the anchor element.

The anchor() function is another cool feature of Anchor Positioning. It allows us to query a position from any anchor element and use it to place an absolutely positioned element.

Uh oh — that doesn’t work. I’ve left this in though, as it’s a educational moment we need to look at.

We hit one of the trickiest issues of Anchor Positioning. In theory, any element on the page can be an anchor using anchor-name and any other element can position itself relative to that anchor. That’s the main purpose of the feature but there are exceptions where an element cannot reference an anchor.

I won’t detail all the cases, but in our example, the pseudo-element (the tail) is a child of the tooltip, which is an absolutely positioned element. This makes the tooltip the containing block of the pseudo-element and prevents it from seeing any anchor defined outside it. (If you think z-index and stacking context are hard, get ready for this)

To overcome this, I will update the position of the pseudo-element to fixed. This changes its containing block (the viewport at the moment) and makes it able to see the anchor element.

Yes, the demo is broken, but drag the anchor close to the edges and see how the tail is correctly placed horizontally as it’s now able to “see” the anchor element. However, the pseudo-element now has a fixed position so it can no longer be placed relatively to its parent element, the tooltip. To fix this we can make the tooltip an anchor element as well, so the pseudo-element can reference it.

In the end we need two anchors: #anchor and #tooltip. The tooltip is positioned relatively to the anchor, and the tail is positioned relatively to both the anchor and the tooltip.

#anchor {
  position: absolute;
  anchor-name: --anchor;
}
#tooltip {
  --d: 1em;  /* distance between anchor and tooltip  */
  --s: 1.2em; /* tail size */
  
  position: absolute; 
  position-anchor: --anchor;
  anchor-name: --tooltip;
}
/* the tail */
#tooltip:before {
  content: "";
  position: fixed;
  z-index: -1;
  width: var(--s);
  /* vertical position from tooltip  */
  top:    calc(anchor(--tooltip top   ) - var(--d));
  bottom: calc(anchor(--tooltip bottom) - var(--d));
  /* horizontal position from anchor */
  left: calc(anchor(--anchor center) - var(--s)/2);
}

Thanks to anchor(), I can retrieve the top and bottom edges of the tooltip element and the center of the anchor element to correctly position the pseudo-element.

Our tooltip is now perfect! As I mentioned in the introduction, this CSS is not particularly complex. We barely used 20 declarations.

What if we want to start with the bottom position?

Easy! You simply change the initial configuration to consider the bottom position and flip-block will switch to the top one when there is an overflow.

#tooltip {
  position-area: bottom; /* instead of position-area: top; */
  top: var(--d); /* instead of bottom: var(--d); */
  margin-bottom: var(--d); /* margin-top: var(--d) */
}

Conclusion

That’s all for this first part. We learned how to place a tooltip using position-area and how to defined a fallback position when an overflow occurs. Not to mention the flip feature and the use of the anchor() function.

In the second part, we will increase the difficulty by working with more than two positions. Take the time to digest this first part before moving to the next one I also invite you to spend a few minutes on my interactive demo of position-area to familiarize yourself with it.

Article Series

]]>
https://frontendmasters.com/blog/perfectly-pointed-tooltips-a-foundation/feed/ 3 7514