Perfectly Pointed Tooltips: All Four Sides
Perfectly Pointed Tooltips: All Four Sides êŽë š
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
Note
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:
CodePen Embed Fallback https://codepen.io/t_afif/pen/QwyMrvG Follow me if you can! (drag the anchor)
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);
}
CodePen Embed Fallback https://codepen.io/t_afif/pen/YPwEVqm adding gap
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):
CodePen Embed Fallback https://codepen.io/t_afif/pen/LEGQXoG/f008b8ee74788eef7c17daf4851cbb4c Untitled
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.

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.
Note
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;
CodePen Embed Fallback https://codepen.io/t_afif/pen/ZYQxKjy/d9b8e34dd58a044957b40a251b0fc415 Untitled
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.

For this, we can use:
justify-self: unsafe anchor-center;
Note
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.
Note
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:

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.
CodePen Embed Fallback https://codepen.io/t_afif/pen/Wbrzjjb/159e2f67a7a6e2b14bacec4fc4f45591 Untitled
And if we add back the left and right positions to the fallbacks, the browser will use them instead of overflowing!
CodePen Embed Fallback https://codepen.io/t_afif/pen/VYeXbVg/02b211bba024d28a17e5ea15846080bc Untitled
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.

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.
CodePen Embed Fallback https://codepen.io/t_afif/pen/vELRZKB/d7845171d64b9390b7a6af08af748512 Untitled
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.
Note
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-center. normal being the default value of justify-self.
CodePen Embed Fallback https://codepen.io/t_afif/pen/MYKGWZX/38a52cdc861f42c2304930dd7edea196 Untitled
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 --size. min-height becomes a min-width! Clever, right?
Note
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.
CodePen Embed Fallback https://codepen.io/t_afif/pen/qEbYBge/3c340988dad465c415beb2f57778f6bb Untitled
Now our tooltip position is perfect! Letâs add the tail.
Adding The Tail
First, we create a shape that contains the 4 tails.

#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;

And for the other sides, we do nothing! The flip will do the job for us.
CodePen Embed Fallback https://codepen.io/t_afif/pen/QwyMrvG Follow me if you can! (drag the anchor)
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