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

推荐订阅源

Security Archives - TechRepublic
Security Archives - TechRepublic
P
Privacy & Cybersecurity Law Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
W
WeLiveSecurity
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
L
LINUX DO - 热门话题
C
Cybersecurity and Infrastructure Security Agency CISA
S
Security Affairs
Latest news
Latest news
Security Latest
Security Latest
N
News and Events Feed by Topic
Spread Privacy
Spread Privacy
P
Proofpoint News Feed
T
The Blog of Author Tim Ferriss
Y
Y Combinator Blog
Google DeepMind News
Google DeepMind News
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
The Exploit Database - CXSecurity.com
The Last Watchdog
The Last Watchdog
C
Cyber Attacks, Cyber Crime and Cyber Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
V
Vulnerabilities – Threatpost
Hacker News - Newest:
Hacker News - Newest: "LLM"
Microsoft Azure Blog
Microsoft Azure Blog
V
Visual Studio Blog
The Cloudflare Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
G
GRAHAM CLULEY
博客园_首页
S
Secure Thoughts
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
AWS News Blog
AWS News Blog
腾讯CDC
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The Register - Security
The Register - Security
N
News and Events Feed by Topic
A
Arctic Wolf
MongoDB | Blog
MongoDB | Blog
爱范儿
爱范儿
Project Zero
Project Zero
A
About on SuperTechFans
罗磊的独立博客
云风的 BLOG
云风的 BLOG
Know Your Adversary
Know Your Adversary
S
Security @ Cisco Blogs
Google Online Security Blog
Google Online Security Blog
K
Kaspersky official blog
L
LINUX DO - 最新话题
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
F
Fortinet All Blogs

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) Two Circles, One Arrow, and Anchor Positioning 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 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
Custom Progress Element Using Anchor Positioning & Scroll-Driven Animations
Temani Afif on November 13, 2024 · 2024-11-13 · via CSS Articles by Temani Afif

In a previous article, we made a cool CSS-only range slider powered by anchor positioning and scroll-driven animations. Using minimal HTML and a few CSS tricks we created something that would have required a lot of JavaScript if we built it 2 years ago.

In this article, we will do the same with the <progress> element and try to make it as cool as the range slider above.

Article Series

Enough suspense! Here is a demo of what we are making (it’s animated, so hit Rerun if you missed it).

Cool, right? Don’t search for the hidden JavaScript code because there is none. As for the HTML, it’s nothing but the <progress> element. This leaves us with complex CSS, which is admittedly a bit hard to decipher. But that’s what we’re here for, so let’s dissect it!

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

I highly recommend you read the previous article before this one. It’s not mandatory but I will be reusing many CSS tricks so this one will be easier to understand if you already know some of the tricks.

The Initial Configuration

We said that the HTML is as simple as the <progress> element, which is true, but it’s more complex because this native element has an internal structure that is browser-specific. It’s one of those situations where we need to use different vendor prefixes and repeat the style more than once.

HTML Structure

Here is the structure when using Chrome, Safari, and Edge:

<progress>
 <div pseudo="-webkit-progress-inner-element">
   <div pseudo="-webkit-progress-bar">
     <div pseudo="-webkit-progress-value"></div>
   </div>
 </div>
<progress>Code language: HTML, XML (xml)

And the one when using Firefox:

<progress>
  <div pseudo="-moz-progress-bar"></div>
</progress>Code language: HTML, XML (xml)

For the sake of simplicity, I will only consider the first structure in this article. When Firefox has better support for the features, I will update the article.

CSS Structure

Let’s start with some basic styling.

progress {
  width: 200px;
  height: 40px;
  appearance: none;
}
progress::-webkit-progress-value {
  background: #7AB317;
}Code language: CSS (css)

Nothing fancy so far. Let’s disable the default appearance, add some dimension, and color the progress. It’s the same color for all, but later we will have custom colors.

That’s it for the initial configuration, let’s move to the interesting parts!

To create the tooltip I will rely on the ::before pseudo-element (or the ::after if you want) and I will pick the code of shape from my online collection. I will be using #5 and #6 but you have up to 100 choices!

progress {
  position: relative;
}
progress::before {
  position: absolute;
  content: "00%";
  /* 
     the code of the tooltip shape 
     copied from the generator 
  */
}Code language: CSS (css)

Anchor Positioning

Now we have to position the tooltip correctly and here enter Anchor Positioning. This is probably the easiest part and here is the code:

progress::-webkit-progress-value {
  anchor-name: --progress;
}
progress::before {
  position: absolute;
  position-anchor: --progress;
  position-area: top right;
}
progress.bottom::before {
  position-area: bottom right;
}Code language: CSS (css)

Even if you are unfamiliar with the feature, the code should be self-explanatory. The progress value is the anchor, and the pseudo-element is relatively positioned to that anchor. Then we define the position to be top right (or bottom right)

The result so far:

Still not perfect but we can already see that the tooltip is following the progression. We need to rectify the position to make sure the tail is aligned with the corner. A simple translation can fix this:

progress::-webkit-progress-value {
  anchor-name: --progress;
}
progress::before {
  position-anchor: --progress;
  position-area: top right;
  /* --h is the variable that controls the height of the tail */
  translate: -50% calc(-1.2*var(--h));
}
progress.bottom::before {
  position-area: bottom right;
  translate: -50% calc(1.2*var(--h));
}Code language: CSS (css)

The logic is similar to the translation you combine with left: 0 or top: 0 to center an element.

Scoping

I would like to note that using position: relative is important here. If you remove it, all the tooltips will be above each other considering the last progress element. This is because I am using the same anchor-name and position: relative will limit the scope of the anchors. It will make sure each anchor is only available to its progress element.

Another property that allows you to control the scope; it is anchor-scope. Instead of position: relative you can do the following:

progress {
  anchor-scope: all;
}Code language: CSS (css)

Scoping is probably the issue you will face the most when working with multiple anchors so don’t forget about it.

Getting The Progress Value

You probably wonder what kind of CSS magic allows me to get the progress value. The magic is called Scroll-Driven Animations. This is the trickiest part because I will be using a feature that is designed to create cool animations on scroll but in this case, has nothing to do with scrolling and isn’t being used to animate. Weird right?

Like with the range slider, I will rely on “view progress timeline”. We can track the position of an element (the subject) inside a container (the scroller). With the range slider, we had the thumb that we can slide/move with the mouse and here we have the progress value.

But the progress value is static, it doesn’t move. How can we track the position of a fixed element!?

It doesn’t move but it has a different size based on the progression (more precisely a different width) and this is enough to make it have a different position each time. I know it’s a bit confusing so let’s make a figure.

We have three progress elements with different progression. Considering the structure we saw previously, the progress value is the green element (the ::-webkit-progress-value) having a width relative to the main element (the <progress>). In all the cases, the progress value is always placed at the left which means the distance between its right edge and the right edge of the main element is variable.

That distance is the key here because it can be interpreted as a movement. It’s like at 100% of progression the progress value is at right: 0 and if we decrease the progression, it moves to the left until it reaches right: 100% at 0% of progression. We can express this using Scroll-Driven animations and convert that distance/movement into a value!

@property --x {
  syntax: '<integer>';
  inherits: true;
  initial-value: 0; 
}
progress {
  animation: x linear;
  animation-timeline: --progress;
  timeline-scope: --progress;
  animation-range: entry 100% exit 100%;
}
@keyframes x {
  0%   {--x: 100}
  100% {--x: 0  }
}
progress::-webkit-progress-bar {
  overflow: auto;
}
progress::-webkit-progress-value {
  view-timeline: --progress inline;
}Code language: CSS (css)

We first define the subject by applying view-timeline to the progress value. We have a horizontal movement so we consider the inline axis. Then, we define the scroller by adding overflow: auto (or overflow: hidden).

Why use the ::-webkit-progress-bar instead of the progress?

Technically, both are the same since both have the same width and behave as a container for the progress value (the subject) but remember the tooltip element which is the ::before pseudo-element. If we apply overflow to progress, we will hide it.

After that, we define a linear animation that animates an integer variable from 100 to 0. Then we use animation-timeline to link the animation with the view-timeline we defined on the subject. The last piece of the puzzle is the use of animation-range which is the trickiest part so here is a figure to understand better.

From the MDN page, we can read:

entry Represents the range of a named view progress timeline from the point where the subject element first starts to enter the scroll port (0% progress), to the point where it has completely entered the scroll port (100%).

When we have a 100% progression, the progress value is placed at the right and is completely visible so we can consider “it has completely entered the scroll port (100%)” hence the use of entry 100%.

exit Represents the range of a named view progress timeline from the point where the subject element first starts to exit the scroll port (0% progress), to the point where it has completely exited the scroll port (100%).

When we have a 0% progression, the progress value has a width equal to 0 so both their right and left edges are touching the left edge of the scroller so we can consider “it has completely exited the scroll port (100%).” hence the use of exit 100%.

This means that when we have a 100% progression, the animation is at 0%, and --x is equal to 100. When we have a 0% progression the animation is at 100% and --x is equal to 0. In other words, --x will contain the progress value we want!

If you are a bit lost, don’t worry. We are dealing with a new feature and new concepts so it requires a lot of practice to get used to them. For this reason, I invite you to read the previous article so you have more examples to study. I also went a bit fast here because I already explained a lot of stuff there (like the use of timeline-scope).

Finally, we show the value within the pseudo element using a counter.

progress::before {
  content: counter(val) "%";
  counter-reset: val var(--x);
}Code language: CSS (css)

Let’s improve the coloration now. We can use the value of --x combined with color-mix() to create a dynamic coloration.

progress {
  --_c: color-mix(in hsl, #E80E0D, #7AB317 calc(1% * var(--x)));
}Code language: CSS (css)

When --x is equal to 0 we get color-mix(in hsl,#E80E0D,#7AB317 0%) and the first color is used. When --x is equal to 100 we get color-mix(in hsl,#E80E0D,#7AB317 100%) and the second color is used. When we have a value between 0 and 100 we get a mix of both colors and that mix will depend on the progression!

The color is stored within a variable --_c so we can easily use it in different places. In our case, it will color the tooltip and the progress value.

Our progress element is now perfect!

Take the time to digest what you have learned so far before moving to the next section. Consider this as a checkpoint because we have done the important parts. What comes next is some fancy animations and another example for you to study as homework.

Adding The Animation

Here is the demo I shared in the introduction to remind you about the animation we are making:

I had an idea to animate the width of the progress value for 0 to its defined width. The tooltip is anchored to the progress value and --x depends on that width so it should be easy. Unfortunately, It doesn’t work. For some reason, I cannot apply an animation to the progress value. It’s probably due to the essential nature of the element.

Here is a simplified demo illustrating what I tried and didn’t work. Maybe some of you can find out what’s wrong.

To overcome this limitation, I will define a new animation within the main element as follows:

@property --y {
  syntax: '<number>';
  inherits: true;
  initial-value: 1; 
}
progress {
  animation: y 2s .5s both;
}
@keyframes y {
  0%   {--y: 0}
  100% {--y: 1}
}Code language: CSS (css)

Then I will use the --y variable within the properties that need to animate.

I will start with the progress value where I will create a gradient animation instead of a simple coloration. I will update the below:

progress::-webkit-progress-value {
  background: var(--_c);
}Code language: CSS (css)

With the following:

progress::-webkit-progress-value {
  background: 
    conic-gradient(var(--_c) 0 0)
    0/calc(var(--y)*100%) 100% no-repeat;
}Code language: CSS (css)

When --y will animate, the width of the gradient will also animate from 0% to 100% creating the first animation

If you are wondering what’s going on with that gradient syntax, check this “How to correctly define a one-color gradient

Now, we need to do the same with the tooltip position. We update the following:

progress::before {
  position-area: top right;
}Code language: CSS (css)

With

progress::before {
  position-area: top center;
  justify-self: unsafe start;
  left: calc(100% * var(--y));
}Code language: CSS (css)

We need the tooltip to slide the whole progress value so we have to consider a new position area, which is top center. Then, the left property will animate from 0% to 100%.

The use of position-area: top center will apply a default alignment for the tooltip that we need to override to be able to use left. That’s the purpose of justify-self: start.

As for the unsafe keyword, it’s related to a quirk you will face at least once when working with anchor positioning. In the specification, you can read:

If the box overflows its inset-modified containing block, but would still fit within its original containing block, by default it will “shift” to stay within its original containing block, even if that violates its normal alignment.

To make it easy, there is a mechanism that may change the element’s position to keep it within specific boundaries. This can be useful in some cases but not here that’s why I am using unsafe to disable that behavior. You can try removing that value and see what is happening.

We are almost done. We are missing the traction effect and the value animation. They are the easiest part:

progress::before {
  content: counter(val) "%";
  counter-reset: val calc(var(--y) * var(--x));
  animation: rotate 2s .5s both cubic-bezier(.18,.4,.8,1.9);
}
@keyframes rotate {
  50% { rotate: calc(var(--x) * -.2deg) }
}Code language: CSS (css)

Inside the counter, we use calc(var(--y) * var(--x)) instead of var(--x) to animate the value and we consider another animation to animate the rotate property based on the --x value.

All the tooltips will spend the same amount of time to travel different distances so to have a realistic traction effect the rotation needs to get bigger if the distance is bigger (if the value of progress is bigger) that’s why rather than using a fixed angle value, I am using a dynamic value that depends on --x.

It’s probably very subtle but if you run the demo many times and look closely you will notice the difference. The use of cubic-bezier is also important because it adds that braking effect at the end.

We did it! A cool CSS-only effect using only the <progress> element.

One More Example: Circular Progress Elements

Don’t leave yet! It’s time for your homework. Here is another demo where I transform the progress element into a circular one. It’s your turn to dissect the code and try to understand what’s happening. If you want a real challenge, try to build it alone before checking my code!

And here is the version with the animation where I am simply reusing the same techniques detailed previously.

Conclusion

I hope you enjoyed this CSS experimentation. It was a good exercise and we explored a lot of modern features. You will probably not use these components in a real project but you will for sure need some of the CSS tricks you have learned.

Article Series