惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

G
Google Developers Blog
S
Schneier on Security
The Hacker News
The Hacker News
P
Proofpoint News Feed
Spread Privacy
Spread Privacy
L
LINUX DO - 热门话题
L
Lohrmann on Cybersecurity
I
Intezer
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Schneier on Security
Schneier on Security
Security Latest
Security Latest
AWS News Blog
AWS News Blog
B
Blog RSS Feed
Microsoft Security Blog
Microsoft Security Blog
有赞技术团队
有赞技术团队
博客园 - 叶小钗
The Last Watchdog
The Last Watchdog
O
OpenAI News
月光博客
月光博客
Hacker News: Ask HN
Hacker News: Ask HN
阮一峰的网络日志
阮一峰的网络日志
S
Security @ Cisco Blogs
Google Online Security Blog
Google Online Security Blog
云风的 BLOG
云风的 BLOG
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Latest news
Latest news
P
Palo Alto Networks Blog
Last Week in AI
Last Week in AI
M
MIT News - Artificial intelligence
Google DeepMind News
Google DeepMind News
P
Proofpoint News Feed
C
CERT Recently Published Vulnerability Notes
Apple Machine Learning Research
Apple Machine Learning Research
U
Unit 42
PCI Perspectives
PCI Perspectives
博客园 - 聂微东
SecWiki News
SecWiki News
宝玉的分享
宝玉的分享
Forbes - Security
Forbes - Security
H
Heimdal Security Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Hugging Face - Blog
Hugging Face - Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Troy Hunt's Blog
博客园 - 三生石上(FineUI控件)
Application and Cybersecurity Blog
Application and Cybersecurity Blog
罗磊的独立博客
WordPress大学
WordPress大学
D
Darknet – Hacking Tools, Hacker News & Cyber Security

CSS Articles by Temani Afif

Get Ready For the Powerful CSS border-shape Property! | CSS-Tricks Let’s Play With Gap Decorations! How to Control Infinite CSS Animations (Part 2 of 2) How to Control Infinite CSS Animations (Part 1 of 2) Making a Responsive Pyramidal Grid With Modern CSS | CSS-Tricks How to Create a CSS-only Elastic Text Effect Making Complex CSS Shapes Using shape() | CSS-Tricks Responsive Hexagon Grid Using Modern CSS | CSS-Tricks Responsive List of Avatars Using Modern CSS (Part 2) | CSS-Tricks Responsive List of Avatars Using Modern CSS (Part 1) | CSS-Tricks Perfectly Pointed Tooltips: To The Corners Perfectly Pointed Tooltips: All Four Sides Perfectly Pointed Tooltips: A Foundation Sequential linear() Animation With N Elements | CSS-Tricks Infinite Marquee Animation using Modern CSS Better CSS Shapes Using shape() — Part 4: Close and Move | CSS-Tricks Drawing CSS Shapes using corner-shape Better CSS Shapes Using shape() — Part 3: Curves | CSS-Tricks Better CSS Shapes Using shape() — Part 2: More on Arcs | CSS-Tricks Better CSS Shapes Using shape() — Part 1: Lines and Arcs | CSS-Tricks Creating Blob Shapes using clip-path: shape() Creating Flower Shapes using clip-path: shape() Custom progress element using the attr() function A CSS-Only Star Rating Component and More! (Part 2) | CSS-Tricks A CSS-Only Star Rating Component and More! (Part 1) | CSS-Tricks How to Create Wavy Boxes Using CSS Full-Bleed Layout with Modern CSS Fancy Menu Navigation Using Anchor Positioning | CSS-Tricks How to Create a Zig-Zag Box Using CSS How to Create Zig-Zag CSS Loaders Using One Element Custom Progress Element Using Anchor Positioning & Scroll-Driven Animations How to Create Filling CSS Loaders Using One Element How to Create Curved-Edge and Rounded-Edge Shapes Using CSS CSS Tricks That Use Only One Gradient | CSS-Tricks How to create Shapes with Inner Curves using CSS Mask Custom Range Slider Using Anchor Positioning & Scroll-Driven Animations How to Get the Width/Height of Any Element in Only CSS How Keyboard Navigation Works in a CSS Game How To Create Cut-Out Shapes using The clip-path property The Modern Guide For Making CSS Shapes — Smashing Magazine css-shape.com: The Ultimate Collection of CSS-only Shapes ⚡️ Sliding 3D Image Frames In CSS — Smashing Magazine CSS Tricks To Master The clip-path Property Creating Wavy Circles with Fancy Animations in CSS Modern CSS Tooltips And Speech Bubbles (Part 2) — Smashing Magazine Do you need a Tooltip or a Speech Bubble? I have created 100 using CSS 😲 Modern CSS Tooltips And Speech Bubbles (Part 1) — Smashing Magazine Creating Flower Shapes using CSS Mask & Trigonometric Functions CSS Shapes: The Triangle The Complex But Awesome CSS border-image Property — Smashing Magazine CSS Tricks to add 3D Effects to your Text CSS Responsive Multi-Line Ribbon Shapes (Part 2) — Smashing Magazine CSS Responsive Multi-Line Ribbon Shapes (Part 1) — Smashing Magazine I have made 100+ CSS-only Ribbon Shapes | The Perfect Collection 🎀 How to create a CSS-only infinite scroll animation Re-Creating The Pop-Out Hover Effect With Modern CSS (Part 2) — Smashing Magazine Re-Creating The Pop-Out Hover Effect With Modern CSS (Part 1) — Smashing Magazine How to Create CSS Ribbon Shapes with a Single Element CSS Shapes: The Ribbon Revealing Images With CSS Mask Animations — Smashing Magazine css-loaders.com: The Biggest Collection of Loading Animations (more than 500 🤯) How To Define An Array Of Colors With CSS — Smashing Magazine CSS effects on images II Shines, Perspective, And Rotations: Fancy CSS 3D Effects For Images — Smashing Magazine What's your Lucky CSS Pattern? Check It Now! How to Add a CSS Reveal Animation to Your Images Modern Layouts using CSS Grid I created 100+ unique CSS patterns | The best collection 🤩 How to Create a Custom Range Slider Using CSS CSS Shapes: The Heart CSS Shapes: Polygon & Starburst A Text Reveal Animation using CSS CSS Tip: learn CSS the easy way! How to build a CSS-only accordion How to create Breadcrumb Navigation with CSS Different Ways to Get CSS Gradient Shadows | CSS-Tricks A CSS-only responsive Stepper component A Fancy Hover Effect For Your Avatar | CSS-Tricks How to make a zoom effect using CSS Creating a Custom Cursor using CSS CSS Infinite 3D Sliders | CSS-Tricks CSS Infinite Slider Flipping Through Polaroid Images | CSS-Tricks How to create a responsive sidebar menu using CSS CSS Infinite and Circular Rotating Image Slider | CSS-Tricks Making Static Noise From a Weird CSS Gradient Bug | CSS-Tricks CSS Grid and Custom Shapes, Part 3 | CSS-Tricks Fancy Image Decorations: Outlines and Complex Animations | CSS-Tricks Fancy Image Decorations: Masks and Advanced Hover Effects | CSS-Tricks Fancy Image Decorations: Single Element Magic | CSS-Tricks How to create an infinite image slider using CSS How to Create Wavy Shapes & Patterns in CSS | CSS-Tricks How I Made a Pure CSS Puzzle Game | CSS-Tricks How to create a Tooltip/Speech Bubble using CSS CSS Grid and Custom Shapes, Part 2 | CSS-Tricks CSS Grid and Custom Shapes, Part 1 | CSS-Tricks Zooming Images in a Grid Layout | CSS-Tricks How to create a CSS-only loader with one element Exploring CSS Grid’s Implicit Grid and Auto-Placement Powers | CSS-Tricks How to create background pattern using CSS & conic-gradient Single Element Loaders: The Bars | CSS-Tricks
Two Circles, One Arrow, and Anchor Positioning
Temani Afif on March 23, 2026 · 2026-03-23 · via CSS Articles by Temani Afif

In a previous series of articles, we studied a classic use case of anchor positioning: Tooltips. In this article, we will explore a less common use case that is a great example of the power of this new feature and modern CSS in general.

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

Let’s start with a demo. Drag both circles and see how the arrow follows the movement. You will also get the distance between the two circles inside the arrow shape. And if the circles are too close, the arrow shape changes!

Except for the drag feature, everything else is controlled using CSS. The position and shape of the arrow, the distance calculation, collision/proximity detection, etc. I know it’s hard to believe, but CSS has evolved a lot to make this possible!

“Why CSS?!”

The classic question that always emerges in such situations. You might think this use case is not suitable for a CSS-only approach, and I agree. There are plenty of tools/libraries that rely on JavaScript and/or SVG, which do the job perfectly. So use those if you need this kind of feature.

My CSS-only demo can be seen as an experiment or an exploration of new CSS features. The goal is not to build that demo, but the steps I followed and the tricks I used to build it. Pushing the limit of CSS and creating things that sound “impossible” is the best way to learn CSS. I say “impossible” because there is plenty of stuff that I was not able to build, but when I succeed, I am here to write an article about it.

If you are eager to learn cool CSS tricks and discover some modern features, you are in the right place!

The Initial Configuration

The main idea is that one element can be anchored to multiple elements (two in our case).

:nth-child(1 of .circle) {
  anchor-name: --c1;
}
:nth-child(2 of .circle) {
  anchor-name: --c2;
}
.arrow {
  position: absolute;
  top:    min(anchor(--c1 top),   anchor(--c2 top));
  left:   min(anchor(--c1 left),  anchor(--c2 left));
  bottom: min(anchor(--c1 bottom),anchor(--c2 bottom));
  right:  min(anchor(--c1 right), anchor(--c2 right));
  outline: 5px solid #000;
}Code language: CSS (css)

The above code creates a rectangle around both circles. It’s actually the smallest rectangle that can hold both circles. Drag the circles in the demo below and see how it works.

Each side of the rectangle will consider the minimum value from both circles. To better understand the trick, reason through each side individually. For example, the top value needs to match the highest circle’s top value, and that circle will logically have the smallest top value.

We can still optimize the code a little and use the following:

.arrow {
  position: absolute;
  top:    min(anchor(--c1 inside), anchor(--c2 inside));
  left:   min(anchor(--c1 inside), anchor(--c2 inside));
  bottom: min(anchor(--c1 inside), anchor(--c2 inside));
  right:  min(anchor(--c1 inside), anchor(--c2 inside));
}Code language: CSS (css)

The value inside is a special value that refers to the same side as the one where it’s used. With top, it’s equal to top; with left, it’s equal to left, and so on.

This gives us the same value for all the properties that we can turn into a variable:

.arrow {
  position: absolute;
  --p: min(anchor(--c1 inside), anchor(--c2 inside));
  top: var(--p);
  left: var(--p);
  bottom: var(--p);
  right: var(--p);
}Code language: CSS (css)

Or simply use the inset property:

.arrow {
  position: absolute;
  inset: min(anchor(--c1 inside), anchor(--c2 inside));
}Code language: CSS (css)

Let’s update the inset property and make the rectangle start at the center of the circles:

.arrow {
  position: absolute;
  inset: min(anchor(--c1 inside) + anchor-size(--c1)/2,
             anchor(--c1 inside) + anchor-size(--c2)/2);
}Code language: CSS (css)

By adding half the distance to each side, we are decreasing the size of the rectangle, and we get the following:

Do you start to see where we are going? With barely two or three CSS properties, we can already see a link between the two circles.

Linking the Circles

Now that I have a rectangle that follows the position of both circles, the first idea that came to my mind was to “shape” that rectangle to make it look like an arrow.

We have clip-path so let’s start with a simple shape:

.arrow {
  clip-path: polygon(0 0, 15px 0,100% calc(100% - 15px), 100% 100%, calc(100% - 15px) 100%,0 15px);
}Code language: CSS (css)

Not bad at first glance, but once you start dragging the circles around, it looks messy.

Three sets of colored circles and lines with labels: 'Good' in green, 'not bad' in gray, and 'Ugly!' in red.

The shape I created will link to the top-left corner of the rectangle with the bottom-right corner, but this is not ideal, as we can have four different positions (as illustrated below) and this is only good for two of them (A and D).

We need to adjust the shape when we get into the B and C positions. The question is: How do we know in which position we are?

In theory, we can do the following:

--x: sign(anchor(--c1 left) - anchor(--c2 left));
--y: sign(anchor(--c1 top) - anchor(--c2 top));Code language: CSS (css)

When we are at position A, the first circle will have a smaller left value and a smaller top value, hence --x will be equal to -1 and --y equal to -1. For position B, we get 1 and -1, and so on.

Based on the values of --x and --y, we can conditionally have a different shape per position, but unfortunately, it is not possible because the anchor() function can only be used with the inset properties. This will probably change in a future (I hope so), but until then we need another solution.

The only solution I have found is to have an element per position, hence the HTML code of the arrow will look like below:

<div class="arrow">
  <a></a>
  <b></b>
  <c></c>
  <d></d>
</div>Code language: HTML, XML (xml)

Then, we define each position as follows:

.arrow > * {
  position: absolute;
  --_x: calc(anchor(--c1 inside) + anchor-size(--c1)/2);
  --_y: calc(anchor(--c2 inside) + anchor-size(--c2)/2);
}
.arrow :is(a,b) {top:  var(--_x); bottom: var(--_y)}
.arrow :is(a,c) {left: var(--_x); right:  var(--_y)}
.arrow :is(c,d) {top:  var(--_y); bottom: var(--_x)}
.arrow :is(b,d) {left: var(--_y); right:  var(--_x)}Code language: CSS (css)

We no longer need to use min() but instead, we use both values that I am defining as --_x and --_y. The code may look complex at first glance, but if you refer to the previous figure, you will understand its logic.

Positions A and B share the same vertical position for both circles, so they have the same top and bottom value. A and C share the same horizontal position, hence the same left and right value, etc.

But, will we have four elements visible at the same time?

Not really. For example, the <a> element has the following inset values:

/* pseudo-code */
a {
  top: top_of_c1;
  bottom: bottom_of_c2;
  left: left_of_c1;
  right: right_of_c2;
}Code language: CSS (css)

If we are at position A, it will be visible, but if we are at position B, you won’t see it because left_of_c1 will be bigger than the right_of_c2. When the right value is bigger than the left value the browser makes them equal, which means the element has a width equal to 0 hence invisible.

I think a demo is worth a thousand words. If you drag the circles, you can see four different colors for each element, and only one color is visible at a time.

Another observation we can make is that each position can be defined as a flip of another one. If we flip A vertically, we get B. If we flip it horizontally, we get C, and if we flip it both horizontally and vertically, we get D. This means we are not going to write too much code. We do the job once, and then we flip!

.arrow > * { /* the same code for all */ }

/* We flip the other positions */
.arrow b {scale: -1  1} /* vertically */
.arrow c {scale:  1 -1} /* horizontally */
.arrow d {scale: -1 -1} /* horizontally & vertically */Code language: CSS (css)

Let’s try again with the previous clip-path:

Much better: the shape links the two circles, regardless of their positions. Now let’s work on improving that shape and creating a perfect arrow.

Creating the Arrow

Using clip-path to create the arrow is the route to follow, but it won’t be enough alone. I made a first attempt with only clip-path, and it worked, but it was spaghetti code with a lot of math functions. I tried to optimize and came up with better ideas.

Starting here, I will focus only on position A. We can ignore the others, since they are a flipped version of position A, as I explained previously.

Now, instead of trying to shape the <a> element (as well as <b>, <c>, and <d>), I will consider its pseudo-element. The element is a rectangle sized with anchor positioning, so I can make it a container and work “inside” it by using its width/height, thanks to container query units.

Knowing the dimensions of the rectangle will make it a lot easier to create and position the arrow. We cannot rely on anchor() and anchor-size() to get them, so we will use container queries! That’s why I’m using the pseudo-element, as I cannot retrieve the values from the container itself.

First, we start by creating an arrow, and we position it at the bottom of the rectangle:

I will skip the creation of the shape, as it’s a basic usage of clip-path with a few variables to control the shape.

A graphic illustrating a horizontal arrow shape labeled 'before' with dimensions of 254.6 x 52 pixels.

The next step is to rotate the arrow so that it points to the first circle:

Diagram comparing two geometric shapes, with the left side showing a diagonal line between two circles and the right side illustrating dimensions labeled as height, width, and angle.

The angle of rotation is equal to the inverse tangent of the height divided by the width, which translates into the following CSS code:

rotate: atan(100cqh/100cqw);Code language: CSS (css)

Then we adjust the arrow’s width. We need to make it taller so that it reaches the center of the first circle.

Comparison of two shapes: left shows a turquoise circle and an orange circle within dashed rectangles, with a diagonal line representing distance; right illustrates height and width measurements from corners of rectangles.

The value we are looking for is simply the diagonal we can find using the CSS hypotenuse function.

width: hypot(100cqh, 100cqw)Code language: CSS (css)

Our arrow is now perfect!

Oops, it’s not really perfect.

If you drag the circles, you will see a good arrow pointing between both circles, but two others are also visible. Don’t forget that we have four elements for each position and are using four pseudo-elements. The elements can have their size equal to 0, but since we are defining the size of the pseudo-elements, they will remain visible.

To fix this, I will play with their opacity using the following code:

opacity: calc(sign(1cqh)*sign(1cqw))Code language: CSS (css)

If either the width or the height of the element is 0, it means that either 1cqh or 1cqw will be 0. sign() of 0 is also 0 hence we get opacity: 0 (invisible).

Now we are good. Only one arrow is visible at a time and is perfectly sized and placed!

If we want the arrow to be at the edge of the circles instead of the centers, we can adjust the padding and change the reference of clip-path to be content-box

padding-inline: 85px 50px;
clip-path: polygon() content-box;Code language: CSS (css)

If you inspect the pseudo-element, you will notice that the padding is nothing but half the size of the circles. By changing the reference of the clip-path to content-box I am excluding that area from the shape.

A graphic illustrating two overlapping circles, one in light blue and one in light orange, connected by a translucent arrow with a directional indicator, alongside a label displaying dimensions.

The only drawback here is that I am hardcoding the padding, as I don’t have another alternative to get the circle sizes. For this reason, in the original demo, I am adding the sizes as attributes so I can easily control everything from the HTML code. I am doing the same with the anchor names, by the way.

<div class="circle" name="--c1" size="170px"></div>
<div class="circle" name="--c2" size="100px"></div>

<div class="arrow" x="--c1" y="--c2" size_x="170px" size_y="100px">
 ...
</div>Code language: HTML, XML (xml)

Transforming the Arrow

If you get back to the initial demo, you will notice that the arrow changes to something else when both circles get closer, and the shape disappears completely when both circles touch each other.

To achieve this, we need to apply conditions based on the distance between the circles, which also determines the arrow’s width.

hypot(100cqh, 100cqw)Code language: CSS (css)

We create a variable like the following:

--_m0: sign(hypot(100cqh,100cqw) - value);Code language: CSS (css)

The variable will be equal to either 1 if the distance is greater than the value, -1 if the distance is smaller, and 0 if they are equal. Then we can use if() to define two different shapes based on that variable.

clip-path: if(style(--_m0 = 1):
    /* we draw the arrow */
 else:
    /* we draw another shape */
  );Code language: CSS (css)

I won’t get into the details of the “value” or the code of the shapes because it doesn’t really matter. You can view it as a media query with a specific width at which your layout will switch. You create two shapes and decide the distance at which we switch between them.

As for the collision detection, we need a precise value, which is half the sum of the circle sizes. So we define another variable:

--_m1: sign(hypot(100cqh,100cqw) - (Size_c1 + Size_c2)/2);Code language: CSS (css)

--_m1 is either 0 or -1 when both circles are touching each other, and when it’s the case, we hide the arrow completely. We are already using opacity for a similar thing so we can update it like below:

opacity: calc(sign(1cqw)*sign(1cqh)*var(--_m1))Code language: CSS (css)

If the element’s width or height is 0, or the circles overlap, we hide the pseudo-element.

You have probably noticed that I am using = inside if() and not :. If you are wondering why, read the following post: The Hidden Trick of Style Queries and if(). It’s one of those little tricks that you’d better learn early before getting headaches trying to figure out why your code isn’t working.

Showing the Distance

I am sure you are thinking this part is going to be tricky, but it’s actually the easiest part. We have used one pseudo-element to create the arrow, and we will use the second one to show the distance.

The relevant code is the following:

.arrow :is(a,b,c,d)::after {
  content: counter(d);
  counter-reset: d calc((hypot(100cqh,100cqw) - value)/1px);
}Code language: CSS (css)

I am again using the hypotenuse to calculate the diagonal, then I subtract a specific value from it. The result will be a length that we divide by 1px to get an integer. That integer will serve as a reset value for a counter we display within the content.

That’s all. Now the pseudo-element contains the distance value, and we add some styling to position it above the arrow. I will reuse many of the CSS properties I used for the arrow, like opacity to show/hide it when needed, and rotation so it follows the arrow’s orientation.

As for the “value”, it depends on what you want to show. You may decide to show the distance between the center of the circles or the distance between their edge. It doesn’t change the main logic.

Here is again the full demo with all the stuff together:

The final demo includes a few things I didn’t mention, but you should be able to understand them easily. I focused on the main tricks, and everything else is a matter of preference and visual styling.

Graph Theory

Linking two circles together is good, but can we do more? Of course, we can! It’s pretty straightforward because all you have to do is add as many circles/arrows you want in the HTML code.

Thanks to the use of attributes combined with attr(), I don’t need to touch the CSS, regardless of the number of elements we want to have. I can specify everything on the HTML side:

<!-- we give each circle a name and a size  -->
<div class="circle" name="--a" size="150px"></div>
<div class="circle" name="--b" size="100px"></div>
<div class="circle" name="--c" size="80px"></div>

<!--  each arrow will receive two names and two sizes -->
<div class="arrow" x="--a" y="--b" size_x="150px" size_y="100px">...</div>
<div class="arrow" x="--a" y="--c" size_x="150px" size_y="80px">...</div>
<div class="arrow" x="--b" y="--c" size_x="100px" size_y="80px">...</div>Code language: HTML, XML (xml)

We go fancier and study graph theory by implementing a shortest path algorithm!

In this demo, I am relying on a trick I am detailing in a previous article that I highly recommend you read: How to Get the Width/Height of Any Element in Only CSS.

I am using scroll-driven animation to retrieve all the distances between the nodes and make them available at root level. Inside the <arrow> element, you will find a new element <e> that will serve as my size calculator for each link.

For each link, I will define two variables that contain the width and height (e.g., --sax and --say). Then, I calculate the diagonal, which is the distance I need (e.g., –sa). Having all the distances at hand, I calculate the different paths (in our case, four are considered), and we identify the minimum among them. Finally, we conditionally do stuff based on that.

Here is part of the code to see the logic:

:root {
   /* all the distances */
  --sa: hypot(1/var(--sax),1/var(--say));
  --sb: hypot(1/var(--sbx),1/var(--sby));
  --ac: hypot(1/var(--acx),1/var(--acy));
  --ad: hypot(1/var(--adx),1/var(--ady));
  --bc: hypot(1/var(--bcx),1/var(--bcy));
  --bd: hypot(1/var(--bdx),1/var(--bdy));
  --ce: hypot(1/var(--cex),1/var(--cey));
  --de: hypot(1/var(--dex),1/var(--dey));

   /* the different paths */
  --sace: calc(var(--sa) + var(--ac) + var(--ce));
  --sade: calc(var(--sa) + var(--ad) + var(--de));
  --sbce: calc(var(--sb) + var(--bc) + var(--ce));
  --sbde: calc(var(--sb) + var(--bd) + var(--de));
  /* we get the min value */
  --min: min(var(--sace),var(--sade),var(--sbce),var(--sbde));
}
@container style(--sace = var(--min)) {
  /* if S-A-C-E is the shortest path we style the relevant links */
}
@container style(--sade = var(--min)) {
  /* if S-A-D-E is the shortest path we style the relevant links */
}
@container style(--sbce = var(--min)) {
  /* if S-B-C-E is the shortest path we style the relevant links */
}
@container style(--sbde = var(--min)) {
  /* if S-B-D-E is the shortest path we style the relevant links */
}
/* the pseudo element of the header will show the shortest path and the distance */
h3::after {
  content: if(
    style(--sace = var(--min)):"S-A-C-E";
    style(--sade = var(--min)):"S-A-D-E";
    style(--sbce = var(--min)):"S-B-C-E";
    style(--sbde = var(--min)):"S-B-D-E";
  )  " (" counter(d)  ")";
  counter-reset: d round(var(--min));
}Code language: CSS (css)

The implementation is, of course, tailored to a particular HTML structure, and we have to adjust the CSS if we want another graph, but it’s more of a proof of concept and a demo to illustrate that it’s indeed possible. In the future, we may have more features that allow us to have a more dynamic implementation that works with any HTML code (loops in CSS, maybe?)

Conclusion

It was too much for CSS, right? I agree, and again, the main goal was not to create those demos and use them everywhere. It was more about the features and tricks we have learned that will be useful in other situations. We covered a lot of anchor positioning stuff, container queries, if() conditions, various calculations, attr(), etc.