{"id":4369,"date":"2024-11-13T11:56:38","date_gmt":"2024-11-13T16:56:38","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=4369"},"modified":"2025-04-10T11:00:35","modified_gmt":"2025-04-10T16:00:35","slug":"custom-progress-element-using-anchor-positioning-scroll-driven-animations","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/custom-progress-element-using-anchor-positioning-scroll-driven-animations\/","title":{"rendered":"Custom Progress Element Using Anchor Positioning &amp; Scroll-Driven Animations"},"content":{"rendered":"\n<p>In&nbsp;<a href=\"https:\/\/frontendmasters.com\/blog\/custom-range-slider-using-anchor-positioning-scroll-driven-animations\/\">a previous article<\/a>, we made <a href=\"https:\/\/codepen.io\/t_afif\/pen\/MWdmZPL\">a cool CSS-only range slider<\/a> 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.<\/p>\n\n\n\n<p>In this article, we will do the same with the&nbsp;<code>&lt;progress&gt;<\/code>&nbsp;element and try to make it as cool as the range slider above.<\/p>\n\n\n<div class=\"box article-series\">\n  <header>\n    <h3 class=\"article-series-header\">Article Series<\/h3>\n  <\/header>\n  <div class=\"box-content\">\n            <ol>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/custom-progress-element-using-anchor-positioning-scroll-driven-animations\/\">Custom Progress Element Using Anchor Positioning &amp; Scroll-Driven Animations<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/custom-progress-element-using-the-attr-function\/\">Custom progress element using the attr() function<\/a>\n            <\/li>\n                  <\/ol>\n        <\/div>\n<\/div>\n\n\n\n<p>Enough suspense! Here is a demo of what we are making (it&#8217;s animated, so hit Rerun if you missed it).<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_JjQVYgJ\" src=\"\/\/codepen.io\/anon\/embed\/JjQVYgJ?height=450&amp;theme-id=47434&amp;slug-hash=JjQVYgJ&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed JjQVYgJ\" title=\"CodePen Embed JjQVYgJ\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Cool, right? Don\u2019t search for the hidden JavaScript code because there is none. As for the HTML, it\u2019s <em>nothing<\/em> but the&nbsp;<code>&lt;progress&gt;<\/code>&nbsp;element. This leaves us with complex CSS, which is admittedly a bit hard to decipher. But that&#8217;s what we&#8217;re here for, so let\u2019s dissect it!<\/p>\n\n\n\n<p class=\"learn-more\">At the time of writing, only Chrome (and Edge) have the full support of the features we will be using.<\/p>\n\n\n\n<p>I highly recommend you read&nbsp;<a href=\"https:\/\/frontendmasters.com\/blog\/custom-range-slider-using-anchor-positioning-scroll-driven-animations\/\">the previous article<\/a>&nbsp;before this one. It\u2019s 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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-initial-configuration\">The Initial Configuration<\/h2>\n\n\n\n<p>We said that the HTML is as simple as the&nbsp;<code>&lt;progress&gt;<\/code>&nbsp;element, which is true, but it\u2019s more complex because this native element has an internal structure that is browser-specific. It&#8217;s one of those situations where we need to use different vendor prefixes and repeat the style more than once.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">HTML Structure<\/h3>\n\n\n\n<p>Here is the structure when using Chrome, Safari, and Edge:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">progress<\/span>&gt;<\/span>\n <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">pseudo<\/span>=<span class=\"hljs-string\">\"-webkit-progress-inner-element\"<\/span>&gt;<\/span>\n   <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">pseudo<\/span>=<span class=\"hljs-string\">\"-webkit-progress-bar\"<\/span>&gt;<\/span>\n     <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">pseudo<\/span>=<span class=\"hljs-string\">\"-webkit-progress-value\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n   <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">progress<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>And the one when using Firefox:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">progress<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">pseudo<\/span>=<span class=\"hljs-string\">\"-moz-progress-bar\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">progress<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"learn-more\">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.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">CSS Structure<\/h3>\n\n\n\n<p>Let\u2019s start with some basic styling.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">progress<\/span> {\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">200px<\/span>;\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-number\">40px<\/span>;\n  <span class=\"hljs-attribute\">appearance<\/span>: none;\n}\n<span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-pseudo\">::-webkit-progress-value<\/span> {\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-number\">#7AB317<\/span>;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Nothing fancy so far. Let&#8217;s disable the default appearance, add some dimension, and color the progress. It\u2019s the same color for all, but later we will have custom colors.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_ZEgjbOE\/d8f14094e5b64fea444bdc5e84cf2112\" src=\"\/\/codepen.io\/anon\/embed\/ZEgjbOE\/d8f14094e5b64fea444bdc5e84cf2112?height=450&amp;theme-id=47434&amp;slug-hash=ZEgjbOE\/d8f14094e5b64fea444bdc5e84cf2112&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed ZEgjbOE\/d8f14094e5b64fea444bdc5e84cf2112\" title=\"CodePen Embed ZEgjbOE\/d8f14094e5b64fea444bdc5e84cf2112\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>That\u2019s it for the initial configuration, let\u2019s move to the interesting parts!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"adding-the-tooltip\">Adding The Tooltip<\/h2>\n\n\n\n<p>To create the tooltip I will rely on the&nbsp;<code>::before<\/code>&nbsp;pseudo-element (or the&nbsp;<code>::after<\/code>&nbsp;if you want) and I will pick the code of shape from&nbsp;<a href=\"https:\/\/css-generators.com\/tooltip-speech-bubble\/\">my online collection<\/a>. I will be using #5 and #6 but you have up to 100 choices!<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">progress<\/span> {\n  <span class=\"hljs-attribute\">position<\/span>: relative;\n}\n<span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-pseudo\">::before<\/span> {\n  <span class=\"hljs-attribute\">position<\/span>: absolute;\n  <span class=\"hljs-attribute\">content<\/span>: <span class=\"hljs-string\">\"00%\"<\/span>;\n  <span class=\"hljs-comment\">\/* \n     the code of the tooltip shape \n     copied from the generator \n  *\/<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_abejNXO\/ec1ce9f5b47a95f1badfb19a97be62f2\" src=\"\/\/codepen.io\/anon\/embed\/abejNXO\/ec1ce9f5b47a95f1badfb19a97be62f2?height=450&amp;theme-id=47434&amp;slug-hash=abejNXO\/ec1ce9f5b47a95f1badfb19a97be62f2&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed abejNXO\/ec1ce9f5b47a95f1badfb19a97be62f2\" title=\"CodePen Embed abejNXO\/ec1ce9f5b47a95f1badfb19a97be62f2\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Anchor Positioning<\/h3>\n\n\n\n<p>Now we have to position the tooltip correctly and here enter Anchor Positioning. This is probably the easiest part and here is the code:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-pseudo\">::-webkit-progress-value<\/span> {\n  <span class=\"hljs-attribute\">anchor-name<\/span>: --progress;\n}\n<span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-pseudo\">::before<\/span> {\n  <span class=\"hljs-attribute\">position<\/span>: absolute;\n  <span class=\"hljs-attribute\">position-anchor<\/span>: --progress;\n  <span class=\"hljs-attribute\">position-area<\/span>: top right;\n}\n<span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-class\">.bottom<\/span><span class=\"hljs-selector-pseudo\">::before<\/span> {\n  <span class=\"hljs-attribute\">position-area<\/span>: bottom right;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>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&nbsp;<code>top right<\/code>&nbsp;(or&nbsp;<code>bottom right<\/code>)<\/p>\n\n\n\n<p>The result so far:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_wvVxGZj\/eaccd222072e4068f3226aede267cd4d\" src=\"\/\/codepen.io\/anon\/embed\/wvVxGZj\/eaccd222072e4068f3226aede267cd4d?height=450&amp;theme-id=47434&amp;slug-hash=wvVxGZj\/eaccd222072e4068f3226aede267cd4d&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed wvVxGZj\/eaccd222072e4068f3226aede267cd4d\" title=\"CodePen Embed wvVxGZj\/eaccd222072e4068f3226aede267cd4d\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>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:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-pseudo\">::-webkit-progress-value<\/span> {\n  <span class=\"hljs-attribute\">anchor-name<\/span>: --progress;\n}\n<span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-pseudo\">::before<\/span> {\n  <span class=\"hljs-attribute\">position-anchor<\/span>: --progress;\n  <span class=\"hljs-attribute\">position-area<\/span>: top right;\n  <span class=\"hljs-comment\">\/* --h is the variable that controls the height of the tail *\/<\/span>\n  <span class=\"hljs-attribute\">translate<\/span>: -<span class=\"hljs-number\">50%<\/span> <span class=\"hljs-built_in\">calc<\/span>(-<span class=\"hljs-number\">1.2<\/span>*var(--h));\n}\n<span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-class\">.bottom<\/span><span class=\"hljs-selector-pseudo\">::before<\/span> {\n  <span class=\"hljs-attribute\">position-area<\/span>: bottom right;\n  <span class=\"hljs-attribute\">translate<\/span>: -<span class=\"hljs-number\">50%<\/span> <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">1.2<\/span>*var(--h));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The logic is similar to the translation you combine with&nbsp;<code>left: 0<\/code>&nbsp;or&nbsp;<code>top: 0<\/code>&nbsp;to center an element.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_RwXBazZ\/846e3bfdb909680fbe0cd4243dee46f5\" src=\"\/\/codepen.io\/anon\/embed\/RwXBazZ\/846e3bfdb909680fbe0cd4243dee46f5?height=450&amp;theme-id=47434&amp;slug-hash=RwXBazZ\/846e3bfdb909680fbe0cd4243dee46f5&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed RwXBazZ\/846e3bfdb909680fbe0cd4243dee46f5\" title=\"CodePen Embed RwXBazZ\/846e3bfdb909680fbe0cd4243dee46f5\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Scoping<\/h3>\n\n\n\n<p>I would like to note that using&nbsp;<code>position: relative<\/code>&nbsp;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&nbsp;<code>anchor-name<\/code>&nbsp;and&nbsp;<code>position: relative<\/code>&nbsp;will limit the scope of the anchors. It will make sure each anchor is only available to its progress element.<\/p>\n\n\n\n<p>Another property that allows you to control the scope; it is&nbsp;<code>anchor-scope<\/code>. Instead of&nbsp;<code>position: relative<\/code>&nbsp;you can do the following:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">progress<\/span> {\n  <span class=\"hljs-attribute\">anchor-scope<\/span>: all;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Scoping is probably the issue you will face the most when working with multiple anchors so don\u2019t forget about it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"getting-the-progress-value\">Getting The Progress Value<\/h2>\n\n\n\n<p>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&#8217;t being used to animate. Weird right?<\/p>\n\n\n\n<p>Like with&nbsp;<a href=\"https:\/\/frontendmasters.com\/blog\/custom-range-slider-using-anchor-positioning-scroll-driven-animations\/#toc-3\">the range slider<\/a>, I will rely on \u201cview progress timeline\u201d. 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.<\/p>\n\n\n\n<p class=\"learn-more\">But the progress value is static, it doesn\u2019t move. How can we track the position of a fixed element!?<\/p>\n\n\n\n<p>It doesn\u2019t 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\u2019s a bit confusing so let\u2019s make a figure.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"574\" height=\"297\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/11\/s_C72B641192C43333719DB09FE368DD770798849971BE8F1E36E6A0C4EFB7ACED_1730713291487_image.png?resize=574%2C297&#038;ssl=1\" alt=\"\" class=\"wp-image-4370\" style=\"width:637px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/11\/s_C72B641192C43333719DB09FE368DD770798849971BE8F1E36E6A0C4EFB7ACED_1730713291487_image.png?w=574&amp;ssl=1 574w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/11\/s_C72B641192C43333719DB09FE368DD770798849971BE8F1E36E6A0C4EFB7ACED_1730713291487_image.png?resize=300%2C155&amp;ssl=1 300w\" sizes=\"auto, (max-width: 574px) 100vw, 574px\" \/><\/figure>\n<\/div>\n\n\n<p>We have three progress elements with different progression. Considering the structure we saw previously, the progress value is the green element (the&nbsp;<code>::-webkit-progress-value<\/code>) having a width relative to the main element (the&nbsp;<code>&lt;progress&gt;<\/code>). 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.<\/p>\n\n\n\n<p>That distance is the key here because it can be interpreted as a movement. It\u2019s like at&nbsp;<code>100%<\/code>&nbsp;of progression the progress value is at&nbsp;<code>right: 0<\/code>&nbsp;and if we decrease the progression, it moves to the left until it reaches&nbsp;<code>right: 100%<\/code>&nbsp;at&nbsp;<code>0%<\/code>&nbsp;of progression. We can express this using Scroll-Driven animations and convert that distance\/movement into a value!<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@property<\/span> --x {\n  <span class=\"hljs-selector-tag\">syntax<\/span>: '&lt;<span class=\"hljs-selector-tag\">integer<\/span>&gt;';\n  <span class=\"hljs-selector-tag\">inherits<\/span>: <span class=\"hljs-selector-tag\">true<\/span>;\n  <span class=\"hljs-selector-tag\">initial-value<\/span>: 0; \n}\n<span class=\"hljs-selector-tag\">progress<\/span> {\n  <span class=\"hljs-attribute\">animation<\/span>: x linear;\n  <span class=\"hljs-attribute\">animation-timeline<\/span>: --progress;\n  <span class=\"hljs-attribute\">timeline-scope<\/span>: --progress;\n  <span class=\"hljs-attribute\">animation-range<\/span>: entry <span class=\"hljs-number\">100%<\/span> exit <span class=\"hljs-number\">100%<\/span>;\n}\n<span class=\"hljs-keyword\">@keyframes<\/span> x {\n  0%   {<span class=\"hljs-attribute\">--x<\/span>: <span class=\"hljs-number\">100<\/span>}\n  100% {<span class=\"hljs-attribute\">--x<\/span>: <span class=\"hljs-number\">0<\/span>  }\n}\n<span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-pseudo\">::-webkit-progress-bar<\/span> {\n  <span class=\"hljs-attribute\">overflow<\/span>: auto;\n}\n<span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-pseudo\">::-webkit-progress-value<\/span> {\n  <span class=\"hljs-attribute\">view-timeline<\/span>: --progress inline;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We first define the subject by applying&nbsp;<code>view-timeline<\/code>&nbsp;to the progress value. We have a horizontal movement so we consider the inline axis. Then, we define the scroller by adding&nbsp;<code>overflow: auto<\/code>&nbsp;(or&nbsp;<code>overflow: hidden<\/code>).<\/p>\n\n\n\n<p class=\"learn-more\">Why use the&nbsp;<code>::-webkit-progress-bar<\/code>&nbsp;instead of the&nbsp;<code>progress<\/code>?<\/p>\n\n\n\n<p>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&nbsp;<code>::before<\/code>&nbsp;pseudo-element. If we apply overflow to&nbsp;<code>progress<\/code>, we will hide it.<\/p>\n\n\n\n<p>After that, we define a linear animation that animates an integer variable from&nbsp;<code>100<\/code>&nbsp;to&nbsp;<code>0<\/code>. Then we use&nbsp;<code>animation-timeline<\/code>&nbsp;to link the animation with the view-timeline we defined on the subject. The last piece of the puzzle is the use of&nbsp;<code>animation-range<\/code>&nbsp;which is the trickiest part so here is a figure to understand better.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"697\" height=\"389\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/11\/s_C72B641192C43333719DB09FE368DD770798849971BE8F1E36E6A0C4EFB7ACED_1730716083456_image.png?resize=697%2C389&#038;ssl=1\" alt=\"\" class=\"wp-image-4371\" style=\"width:673px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/11\/s_C72B641192C43333719DB09FE368DD770798849971BE8F1E36E6A0C4EFB7ACED_1730716083456_image.png?w=697&amp;ssl=1 697w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/11\/s_C72B641192C43333719DB09FE368DD770798849971BE8F1E36E6A0C4EFB7ACED_1730716083456_image.png?resize=300%2C167&amp;ssl=1 300w\" sizes=\"auto, (max-width: 697px) 100vw, 697px\" \/><\/figure>\n<\/div>\n\n\n<p>From&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/animation-range\">the MDN page<\/a>, we can read:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><code>entry<\/code>&nbsp;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%).<\/p>\n<\/blockquote>\n\n\n\n<p>When we have a 100% progression, the progress value is placed at the right and is completely visible so we can consider \u201c<em>it has completely entered the scroll port (100%)<\/em>\u201d hence the use of&nbsp;<code>entry 100%<\/code>.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><code>exit<\/code>&nbsp;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%).<\/p>\n<\/blockquote>\n\n\n\n<p>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 \u201c<em>it has completely exited the scroll port (100%).<\/em>\u201d hence the use of&nbsp;<code>exit 100%<\/code>.<\/p>\n\n\n\n<p>This means that when we have a 100% progression, the animation is at 0%, and&nbsp;<code>--x<\/code>&nbsp;is equal to 100. When we have a 0% progression the animation is at 100% and&nbsp;<code>--x<\/code>&nbsp;is equal to 0. In other words,&nbsp;<code>--x<\/code>&nbsp;will contain the progress value we want!<\/p>\n\n\n\n<p>If you are a bit lost, don\u2019t 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&nbsp;<a href=\"https:\/\/frontendmasters.com\/blog\/custom-range-slider-using-anchor-positioning-scroll-driven-animations\/\">the previous article<\/a>&nbsp;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&nbsp;<code>timeline-scope<\/code>).<\/p>\n\n\n\n<p>Finally, we show the value within the pseudo element using a counter.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-pseudo\">::before<\/span> {\n  <span class=\"hljs-attribute\">content<\/span>: <span class=\"hljs-built_in\">counter<\/span>(val) <span class=\"hljs-string\">\"%\"<\/span>;\n  <span class=\"hljs-attribute\">counter-reset<\/span>: val <span class=\"hljs-built_in\">var<\/span>(--x);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_poMZdqJ\/a4fb635478d4f07f55d39d0eb9edb9ad\" src=\"\/\/codepen.io\/anon\/embed\/poMZdqJ\/a4fb635478d4f07f55d39d0eb9edb9ad?height=450&amp;theme-id=47434&amp;slug-hash=poMZdqJ\/a4fb635478d4f07f55d39d0eb9edb9ad&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed poMZdqJ\/a4fb635478d4f07f55d39d0eb9edb9ad\" title=\"CodePen Embed poMZdqJ\/a4fb635478d4f07f55d39d0eb9edb9ad\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Let\u2019s improve the coloration now. We can use the value of&nbsp;<code>--x<\/code>&nbsp;combined with&nbsp;<code>color-mix()<\/code>&nbsp;to create a dynamic coloration.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">progress<\/span> {\n  <span class=\"hljs-attribute\">--_c<\/span>: <span class=\"hljs-built_in\">color-mix<\/span>(in hsl, #E80E0D, #<span class=\"hljs-number\">7<\/span>AB317 calc(<span class=\"hljs-number\">1%<\/span> * var(--x)));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>When&nbsp;<code>--x<\/code>&nbsp;is equal to&nbsp;<code>0<\/code>&nbsp;we get&nbsp;<code>color-mix(in hsl,#E80E0D,#7AB317 0%)<\/code>&nbsp;and the first color is used. When&nbsp;<code>--x<\/code>&nbsp;is equal to&nbsp;<code>100<\/code>&nbsp;we get&nbsp;<code>color-mix(in hsl,#E80E0D,#7AB317 100%)<\/code>&nbsp;and the second color is used. When we have a value between&nbsp;<code>0<\/code>&nbsp;and&nbsp;<code>100<\/code>&nbsp;we get a mix of both colors and that mix will depend on the progression!<\/p>\n\n\n\n<p>The color is stored within a variable&nbsp;<code>--_c<\/code>&nbsp;so we can easily use it in different places. In our case, it will color the tooltip and the progress value.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_YzoBmLb\" src=\"\/\/codepen.io\/anon\/embed\/YzoBmLb?height=450&amp;theme-id=47434&amp;slug-hash=YzoBmLb&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed YzoBmLb\" title=\"CodePen Embed YzoBmLb\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Our progress element is now perfect!<\/p>\n\n\n\n<p>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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"adding-the-animation\">Adding The Animation<\/h2>\n\n\n\n<p>Here is the demo I shared in the introduction to remind you about the animation we are making:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_JjQVYgJ\" src=\"\/\/codepen.io\/anon\/embed\/JjQVYgJ?height=450&amp;theme-id=47434&amp;slug-hash=JjQVYgJ&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed JjQVYgJ\" title=\"CodePen Embed JjQVYgJ\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>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&nbsp;<code>--x<\/code>&nbsp;depends on that width so it should be easy. Unfortunately, It doesn&#8217;t work. For some reason, I cannot apply an animation to the progress value. It\u2019s probably due to the essential nature of the element.<\/p>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>Here is a simplified demo illustrating what I tried and didn\u2019t work. Maybe some of you can find out what\u2019s wrong.<\/summary>\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_yLmxaNe\/15d8236f021d0c1a51cc5516c2fa99d4\" src=\"\/\/codepen.io\/anon\/embed\/yLmxaNe\/15d8236f021d0c1a51cc5516c2fa99d4?height=450&amp;theme-id=47434&amp;slug-hash=yLmxaNe\/15d8236f021d0c1a51cc5516c2fa99d4&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed yLmxaNe\/15d8236f021d0c1a51cc5516c2fa99d4\" title=\"CodePen Embed yLmxaNe\/15d8236f021d0c1a51cc5516c2fa99d4\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p><\/p>\n<\/details>\n\n\n\n<p>To overcome this limitation, I will define a new animation within the main element as follows:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@property<\/span> --y {\n  <span class=\"hljs-selector-tag\">syntax<\/span>: '&lt;<span class=\"hljs-selector-tag\">number<\/span>&gt;';\n  <span class=\"hljs-selector-tag\">inherits<\/span>: <span class=\"hljs-selector-tag\">true<\/span>;\n  <span class=\"hljs-selector-tag\">initial-value<\/span>: 1; \n}\n<span class=\"hljs-selector-tag\">progress<\/span> {\n  <span class=\"hljs-attribute\">animation<\/span>: y <span class=\"hljs-number\">2s<\/span> .<span class=\"hljs-number\">5s<\/span> both;\n}\n<span class=\"hljs-keyword\">@keyframes<\/span> y {\n  0%   {<span class=\"hljs-attribute\">--y<\/span>: <span class=\"hljs-number\">0<\/span>}\n  100% {<span class=\"hljs-attribute\">--y<\/span>: <span class=\"hljs-number\">1<\/span>}\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Then I will use the&nbsp;<code>--y<\/code>&nbsp;variable within the properties that need to animate.<\/p>\n\n\n\n<p>I will start with the progress value where I will create a gradient animation instead of a simple coloration. I will update the below:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-pseudo\">::-webkit-progress-value<\/span> {\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">var<\/span>(--_c);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>With the following:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-pseudo\">::-webkit-progress-value<\/span> {\n  <span class=\"hljs-attribute\">background<\/span>: \n    <span class=\"hljs-built_in\">conic-gradient<\/span>(var(--_c) <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">0<\/span>)\n    <span class=\"hljs-number\">0<\/span>\/<span class=\"hljs-built_in\">calc<\/span>(var(--y)*<span class=\"hljs-number\">100%<\/span>) <span class=\"hljs-number\">100%<\/span> no-repeat;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>When&nbsp;<code>--y<\/code>&nbsp;will animate, the width of the gradient will also animate from&nbsp;<code>0%<\/code>&nbsp;to&nbsp;<code>100%<\/code>&nbsp;creating the first animation<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_zYgLXzP\/249f008d99dae9c27d7bf9d730623028\" src=\"\/\/codepen.io\/anon\/embed\/zYgLXzP\/249f008d99dae9c27d7bf9d730623028?height=450&amp;theme-id=47434&amp;slug-hash=zYgLXzP\/249f008d99dae9c27d7bf9d730623028&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed zYgLXzP\/249f008d99dae9c27d7bf9d730623028\" title=\"CodePen Embed zYgLXzP\/249f008d99dae9c27d7bf9d730623028\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>If you are wondering what\u2019s going on with that gradient syntax, check this \u201c<a href=\"https:\/\/css-tip.com\/one-color-gradient\/\">How to correctly define a one-color gradient<\/a>\u201d<\/p>\n\n\n\n<p>Now, we need to do the same with the tooltip position. We update the following:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-pseudo\">::before<\/span> {\n  <span class=\"hljs-attribute\">position-area<\/span>: top right;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>With<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-pseudo\">::before<\/span> {\n  <span class=\"hljs-attribute\">position-area<\/span>: top center;\n  <span class=\"hljs-attribute\">justify-self<\/span>: unsafe start;\n  <span class=\"hljs-attribute\">left<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">100%<\/span> * var(--y));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We need the tooltip to slide the whole progress value so we have to consider a new position area, which is&nbsp;<code>top center<\/code>. Then, the&nbsp;<code>left<\/code>&nbsp;property will animate from&nbsp;<code>0%<\/code>&nbsp;to&nbsp;<code>100%<\/code>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"696\" height=\"298\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/11\/s_C72B641192C43333719DB09FE368DD770798849971BE8F1E36E6A0C4EFB7ACED_1730755343342_image.png?resize=696%2C298&#038;ssl=1\" alt=\"\" class=\"wp-image-4372\" style=\"width:659px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/11\/s_C72B641192C43333719DB09FE368DD770798849971BE8F1E36E6A0C4EFB7ACED_1730755343342_image.png?w=696&amp;ssl=1 696w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/11\/s_C72B641192C43333719DB09FE368DD770798849971BE8F1E36E6A0C4EFB7ACED_1730755343342_image.png?resize=300%2C128&amp;ssl=1 300w\" sizes=\"auto, (max-width: 696px) 100vw, 696px\" \/><\/figure>\n<\/div>\n\n\n<p>The use of&nbsp;<code>position-area: top center<\/code>&nbsp;will apply a default alignment for the tooltip that we need to override to be able to use&nbsp;<code>left<\/code>. That\u2019s the purpose of&nbsp;<code>justify-self: start<\/code>.<\/p>\n\n\n\n<p>As for the&nbsp;<code>unsafe<\/code>&nbsp;keyword, it\u2019s related to a quirk you will face at least once when working with anchor positioning. In&nbsp;<a href=\"https:\/\/www.w3.org\/TR\/css-anchor-position-1\/#position-area\">the specification<\/a>, you can read:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>If the box overflows its inset-modified containing block, but would still fit within its original containing block, by default it will \u201cshift\u201d to stay within its original containing block, even if that violates its normal alignment.<\/p>\n<\/blockquote>\n\n\n\n<p>To make it easy, there is a mechanism that may change the element\u2019s position to keep it within specific boundaries. This can be useful in some cases but not here that\u2019s why I am using&nbsp;<code>unsafe<\/code>&nbsp;to disable that behavior. You can try removing that value and see what is happening.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_yLmqWgK\/731b8478fd78f60a6834eadb0404ea7d\" src=\"\/\/codepen.io\/anon\/embed\/yLmqWgK\/731b8478fd78f60a6834eadb0404ea7d?height=450&amp;theme-id=47434&amp;slug-hash=yLmqWgK\/731b8478fd78f60a6834eadb0404ea7d&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed yLmqWgK\/731b8478fd78f60a6834eadb0404ea7d\" title=\"CodePen Embed yLmqWgK\/731b8478fd78f60a6834eadb0404ea7d\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>We are almost done. We are missing the traction effect and the value animation. They are the easiest part:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-pseudo\">::before<\/span> {\n  <span class=\"hljs-attribute\">content<\/span>: <span class=\"hljs-built_in\">counter<\/span>(val) <span class=\"hljs-string\">\"%\"<\/span>;\n  <span class=\"hljs-attribute\">counter-reset<\/span>: val <span class=\"hljs-built_in\">calc<\/span>(var(--y) * <span class=\"hljs-built_in\">var<\/span>(--x));\n  <span class=\"hljs-attribute\">animation<\/span>: rotate <span class=\"hljs-number\">2s<\/span> .<span class=\"hljs-number\">5s<\/span> both <span class=\"hljs-built_in\">cubic-bezier<\/span>(.<span class=\"hljs-number\">18<\/span>,.<span class=\"hljs-number\">4<\/span>,.<span class=\"hljs-number\">8<\/span>,<span class=\"hljs-number\">1.9<\/span>);\n}\n<span class=\"hljs-keyword\">@keyframes<\/span> rotate {\n  50% { <span class=\"hljs-attribute\">rotate<\/span>: <span class=\"hljs-built_in\">calc<\/span>(var(--x) * -.<span class=\"hljs-number\">2deg<\/span>) }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Inside the counter, we use&nbsp;<code>calc(var(--y) * var(--x))<\/code>&nbsp;instead of&nbsp;<code>var(--x)<\/code>&nbsp;to animate the value and we consider another animation to animate the&nbsp;<code>rotate<\/code>&nbsp;property based on the&nbsp;<code>--x<\/code>&nbsp;value.<\/p>\n\n\n\n<p>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\u2019s why rather than using a fixed angle value, I am using a dynamic value that depends on&nbsp;<code>--x<\/code>.<\/p>\n\n\n\n<p>It\u2019s probably very subtle but if you run the demo many times and look closely you will notice the difference. The use of&nbsp;<code>cubic-bezier<\/code>&nbsp;is also important because it adds that braking effect at the end.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_JjQVYgJ\" src=\"\/\/codepen.io\/anon\/embed\/JjQVYgJ?height=450&amp;theme-id=47434&amp;slug-hash=JjQVYgJ&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed JjQVYgJ\" title=\"CodePen Embed JjQVYgJ\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>We did it! A cool CSS-only effect using only the&nbsp;<code>&lt;progress&gt;<\/code>&nbsp;element.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"one-more-example\">One More Example: Circular Progress Elements<\/h2>\n\n\n\n<p>Don\u2019t leave yet! It\u2019s time for your homework. Here is another demo where I transform the progress element into a circular one. It\u2019s your turn to dissect the code and try to understand what\u2019s happening. If you want a real challenge, try to build it alone before checking my code!<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_PovMVjJ\" src=\"\/\/codepen.io\/anon\/embed\/PovMVjJ?height=450&amp;theme-id=47434&amp;slug-hash=PovMVjJ&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed PovMVjJ\" title=\"CodePen Embed PovMVjJ\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>And here is the version with the animation where I am simply reusing the same techniques detailed previously.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_ZEgjNxm\" src=\"\/\/codepen.io\/anon\/embed\/ZEgjNxm?height=450&amp;theme-id=47434&amp;slug-hash=ZEgjNxm&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed ZEgjNxm\" title=\"CodePen Embed ZEgjNxm\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"conclusion\">Conclusion<\/h2>\n\n\n\n<p>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.<\/p>\n\n\n<div class=\"box article-series\">\n  <header>\n    <h3 class=\"article-series-header\">Article Series<\/h3>\n  <\/header>\n  <div class=\"box-content\">\n            <ol>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/custom-progress-element-using-anchor-positioning-scroll-driven-animations\/\">Custom Progress Element Using Anchor Positioning &amp; Scroll-Driven Animations<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/custom-progress-element-using-the-attr-function\/\">Custom progress element using the attr() function<\/a>\n            <\/li>\n                  <\/ol>\n        <\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>A single HTML `progress` element can have quite an elaborate design with custom colors, a tooltip showing the active %, and even an entrance animation. <\/p>\n","protected":false},"author":12,"featured_media":4374,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[7,32],"class_list":["post-4369","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-css","tag-progress"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/11\/progress.png?fit=1146%2C656&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/4369","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/12"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=4369"}],"version-history":[{"count":7,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/4369\/revisions"}],"predecessor-version":[{"id":5573,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/4369\/revisions\/5573"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/4374"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=4369"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=4369"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=4369"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}