{"id":5537,"date":"2025-04-09T13:39:02","date_gmt":"2025-04-09T18:39:02","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=5537"},"modified":"2025-11-14T11:27:19","modified_gmt":"2025-11-14T16:27:19","slug":"custom-progress-element-using-the-attr-function","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/custom-progress-element-using-the-attr-function\/","title":{"rendered":"Custom progress element using the attr() function"},"content":{"rendered":"\n<p>In&nbsp;<a href=\"https:\/\/frontendmasters.com\/blog\/custom-progress-element-using-anchor-positioning-scroll-driven-animations\/\">a previous article<\/a>, we combined two modern CSS features (<a href=\"https:\/\/frontendmasters.com\/blog\/tag\/anchor\/\">anchor positioning<\/a> and <a href=\"https:\/\/frontendmasters.com\/blog\/tag\/scroll-driven-animations\/\">scroll-driven animations<\/a>) to style the&nbsp;<code>&lt;progress&gt;<\/code>&nbsp;element without extra markup and create a cool component. Here&#8217;s that demo:<\/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=1&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>Anchor positioning was used to correctly place the tooltip shape while scroll-driven animations were used to get the progress value and show it inside the tooltip. Getting the value was the trickiest part of the experimentation. I invite you to read the previous article if you want to understand how scroll-driven animations helps us do it.<\/p>\n\n\n\n<p>In <em>this<\/em> article, we will see an easier way to get our hands on the current value and explore another example of progress element.<\/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<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<h2 class=\"wp-block-heading\" id=\"getting-the-progress-value-using-attr\">Getting the progress value using <code>attr()<\/code><\/h2>\n\n\n\n<p>This is the HTML element we are working with:<\/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> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"4\"<\/span> <span class=\"hljs-attr\">max<\/span>=<span class=\"hljs-string\">\"10\"<\/span>&gt;<\/span><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>Nothing fancy: a progress element where you define the&nbsp;<code>value<\/code>&nbsp;and&nbsp;<code>max<\/code>&nbsp;attribute. Then we use the following CSS:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" 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-attr\">&#91;value]<\/span> {\n  <span class=\"hljs-attribute\">--val<\/span>: <span class=\"hljs-built_in\">attr<\/span>(value type(&lt;number&gt;));\n  <span class=\"hljs-attribute\">--max<\/span>: <span class=\"hljs-built_in\">attr<\/span>(max type(&lt;number&gt;),<span class=\"hljs-number\">1<\/span>);\n\n  <span class=\"hljs-attribute\">--x<\/span>: <span class=\"hljs-built_in\">calc<\/span>(var(--val)\/<span class=\"hljs-built_in\">var<\/span>(--max)); <span class=\"hljs-comment\">\/* the percentage of progression *\/<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><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 waited for this for too long! It\u2019s finally here!<\/p>\n\n\n\n<p>We can use&nbsp;<code>attr()<\/code>&nbsp;function not only with the&nbsp;<code>content<\/code>&nbsp;property but with <em>any<\/em> property including custom properties! The variable&nbsp;<code>--x<\/code>&nbsp;will contain the percentage of progression as a unit-less value in the range&nbsp;<code>[0 1]<\/code>. That\u2019s all \u2014 no complex code needed.<\/p>\n\n\n\n<p>We also have the ability to define the types (<code>number<\/code>, in our case) and specify fallback values. The&nbsp;<code>max<\/code>&nbsp;attribute is not mandatory so if not specified it will default to&nbsp;<code>1<\/code>. Here is the previous demo using this new method instead of scroll-driven animations:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_wBvRYbZ\/525971de0ec4c06a27f76678c2cdee1d\" src=\"\/\/codepen.io\/anon\/embed\/wBvRYbZ\/525971de0ec4c06a27f76678c2cdee1d?height=450&amp;theme-id=1&amp;slug-hash=wBvRYbZ\/525971de0ec4c06a27f76678c2cdee1d&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed wBvRYbZ\/525971de0ec4c06a27f76678c2cdee1d\" title=\"CodePen Embed wBvRYbZ\/525971de0ec4c06a27f76678c2cdee1d\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>If we omit the tooltip and animation parts (explained in&nbsp;<a href=\"https:\/\/frontendmasters.com\/blog\/custom-progress-element-using-anchor-positioning-scroll-driven-animations\/\">the previous article<\/a>), the new code to get the value and use it to define the content of the tooltip and the color is <strong>a lot easier<\/strong>:<\/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\">--val<\/span>: <span class=\"hljs-built_in\">attr<\/span>(value type(&lt;number&gt;));\n  <span class=\"hljs-attribute\">--max<\/span>: <span class=\"hljs-built_in\">attr<\/span>(max type(&lt;number&gt;),<span class=\"hljs-number\">1<\/span>);\n\n  <span class=\"hljs-attribute\">--x<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">100<\/span>*var(--val)\/<span class=\"hljs-built_in\">var<\/span>(--max));\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}\n<span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-pseudo\">::value<\/span> {\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">var<\/span>(--_c);\n}\n<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  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">var<\/span>(--_c);\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<h3 class=\"wp-block-heading\">Should we forget about the \u201ccomplex\u201d scroll-driven animations method?<\/h3>\n\n\n\n<p>Nah \u2014 it can still be useful. Using&nbsp;<code>attr()<\/code>&nbsp;is the best method for this case and probably other cases but scroll-driven animations has one advantage that can be super handy: It can make the progress value available everywhere on the page.<\/p>\n\n\n\n<p>I won\u2019t get into the detail (as to not repeat the previous article) but it has to do with the scope of the timeline. Here is an example where I am showing the progress value within a random element on the page.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_GgRPwwK\/4ea1a6a9699fef4b341f6735c08a37a8\" src=\"\/\/codepen.io\/anon\/embed\/GgRPwwK\/4ea1a6a9699fef4b341f6735c08a37a8?height=450&amp;theme-id=1&amp;slug-hash=GgRPwwK\/4ea1a6a9699fef4b341f6735c08a37a8&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed GgRPwwK\/4ea1a6a9699fef4b341f6735c08a37a8\" title=\"CodePen Embed GgRPwwK\/4ea1a6a9699fef4b341f6735c08a37a8\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>The animation is defined on the&nbsp;<code>html<\/code>&nbsp;element (the uppermost element) which means all the elements will have access to the&nbsp;<code>--x<\/code>&nbsp;variable.<\/p>\n\n\n\n<p>If your goal is to get the progress value and style the element itself then using&nbsp;<code>attr()<\/code>&nbsp;should be enough but if you want to make the value available to other elements on the page then scroll-driven animations is the key.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"progress-element-with-dynamic-coloration\">Progress element with dynamic coloration<\/h2>\n\n\n\n<p>Now that we have our new way to get the value let\u2019s create&nbsp;<a href=\"https:\/\/css-tip.com\/custom-progress\/\">a progress element with dynamic coloration<\/a>. This time, we will not fade between two colors like we did in the previous demo but the color will change based on the value.<\/p>\n\n\n\n<p>A demo worth a thousand words:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_OPJwbVJ\" src=\"\/\/codepen.io\/anon\/embed\/OPJwbVJ?height=450&amp;theme-id=1&amp;slug-hash=OPJwbVJ&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed OPJwbVJ\" title=\"CodePen Embed OPJwbVJ\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>As you can see, we have 3 different colors (red, orange and green) each one applied when the value is within a specific range. We have a kind of conditional logic that we can implement using various techniques.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"using-multiple-gradients\">Using multiple gradients<\/h3>\n\n\n\n<p>I will rely on the fact that a gradient with a size equal to 0 will be hidden so if we stack multiple gradients and control their visibility we can control which color is visible.<\/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><span class=\"hljs-selector-attr\">&#91;value]<\/span> {\n  <span class=\"hljs-attribute\">--val<\/span>: <span class=\"hljs-built_in\">attr<\/span>(value type(&lt;number&gt;));\n  <span class=\"hljs-attribute\">--max<\/span>: <span class=\"hljs-built_in\">attr<\/span>(max type(&lt;number&gt;),<span class=\"hljs-number\">1<\/span>);\n  <span class=\"hljs-attribute\">--_p<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">100%<\/span>*var(--val)\/<span class=\"hljs-built_in\">var<\/span>(--max)); <span class=\"hljs-comment\">\/* the percentage of progression *\/<\/span>\n}\n<span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-attr\">&#91;value]<\/span><span class=\"hljs-selector-pseudo\">::-webkit-progress-value<\/span> {\n   <span class=\"hljs-attribute\">background<\/span>: \n    <span class=\"hljs-comment\">\/* if (p &lt; 30%) \"red\" *\/<\/span>\n    <span class=\"hljs-built_in\">conic-gradient<\/span>(red    <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">0<\/span>) <span class=\"hljs-number\">0<\/span>\/<span class=\"hljs-built_in\">max<\/span>(<span class=\"hljs-number\">0%<\/span>,<span class=\"hljs-number\">30%<\/span> - var(--_p)) <span class=\"hljs-number\">1%<\/span>,\n    <span class=\"hljs-comment\">\/* else if (p &lt; 60%) \"orange\" *\/<\/span>\n    <span class=\"hljs-built_in\">conic-gradient<\/span>(orange <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">0<\/span>) <span class=\"hljs-number\">0<\/span>\/<span class=\"hljs-built_in\">max<\/span>(<span class=\"hljs-number\">0%<\/span>,<span class=\"hljs-number\">60%<\/span> - var(--_p)) <span class=\"hljs-number\">1%<\/span>,\n    <span class=\"hljs-comment\">\/* else \"green\" *\/<\/span>\n    green;\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<p>We have two&nbsp;<a href=\"https:\/\/css-tip.com\/one-color-gradient\/\">single-color gradients<\/a>&nbsp;(red and orange) and a <code>background-color<\/code> (green). If, for example, the progression is equal to 20%, the first gradient will have a size equal to&nbsp;<code>10% 1%<\/code>&nbsp;(visible) and the second gradient will have a size equal&nbsp;<code>40% 1%<\/code>&nbsp;(visible). Both are visible but you will only see the top layer so the color is red. If the progression is equal to 70%, both gradients will have a size equal to&nbsp;<code>0% 1%<\/code>&nbsp;(invisible) and the background-color will be visible: the color is green.<\/p>\n\n\n\n<p>Clever, right? We can easily scale this technique to consider as many colors as you want by adding more gradients. Simply pay attention to the order. The smallest value is for the top layer and we increase it until we reach the bottom layer (the <code>background-color<\/code>).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"using-an-array-of-colors\">Using an array of colors<\/h3>\n\n\n\n<p>A while back I wrote an article on how&nbsp;<a href=\"https:\/\/www.smashingmagazine.com\/2023\/07\/define-array-colors-css\/\">to create and manipulate an array of colors<\/a>. The idea is to have a variable where you can store the different colors:<\/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\">--colors<\/span>: <span class=\"hljs-selector-tag\">red<\/span>, <span class=\"hljs-selector-tag\">blue<\/span>, <span class=\"hljs-selector-tag\">green<\/span>, <span class=\"hljs-selector-tag\">purple<\/span>;<\/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>Then be able to select the needed color using an index. Here is a demo taken from that article.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_KKrNYyp\" src=\"\/\/codepen.io\/anon\/embed\/KKrNYyp?height=450&amp;theme-id=1&amp;slug-hash=KKrNYyp&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed KKrNYyp\" title=\"CodePen Embed KKrNYyp\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>This technique is limited to background coloration but it\u2019s enough for our case.<\/p>\n\n\n\n<p>This time, we are not going to define precise values like we did with the previous method but we will only define the number of ranges.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>If we define N=2, we will have two colors. The first one for the range&nbsp;<code>[0% 50%[<\/code>&nbsp;and the second one for the range&nbsp;<code>[50% 100%]<\/code><\/li>\n\n\n\n<li>If we define N=3, we will have three colors. The first one for&nbsp;<code>[0% 33%[<\/code>, the second for&nbsp;<code>[33% 66%[<\/code>&nbsp;and the last one for&nbsp;<code>[66% 100%]<\/code><\/li>\n<\/ul>\n\n\n\n<p>I think you get the idea and here is a demo with four colors:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_LEYJGoQ\" src=\"\/\/codepen.io\/anon\/embed\/LEYJGoQ?height=450&amp;theme-id=1&amp;slug-hash=LEYJGoQ&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed LEYJGoQ\" title=\"CodePen Embed LEYJGoQ\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>The main trick here is to convert the progress value into an index and to do this we can rely on the&nbsp;<code>round()<\/code>&nbsp;function:<\/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-attr\">&#91;value]<\/span> {\n  <span class=\"hljs-attribute\">--n<\/span>: <span class=\"hljs-number\">4<\/span>; <span class=\"hljs-comment\">\/* number of ranges *\/<\/span>\n  <span class=\"hljs-attribute\">--c<\/span>: <span class=\"hljs-number\">#F04155<\/span>,<span class=\"hljs-number\">#F27435<\/span>,<span class=\"hljs-number\">#7AB317<\/span>,<span class=\"hljs-number\">#0D6759<\/span>;\n  \n  <span class=\"hljs-attribute\">--_v<\/span>: <span class=\"hljs-built_in\">attr<\/span>(value type(&lt;number&gt;));\n  <span class=\"hljs-attribute\">--_m<\/span>: <span class=\"hljs-built_in\">attr<\/span>(max type(&lt;number&gt;),<span class=\"hljs-number\">1<\/span>);\n  <span class=\"hljs-attribute\">--_i<\/span>: <span class=\"hljs-built_in\">round<\/span>(down,<span class=\"hljs-number\">100<\/span>*var(--_v)\/<span class=\"hljs-built_in\">var<\/span>(--_m),<span class=\"hljs-number\">100<\/span>\/<span class=\"hljs-built_in\">var<\/span>(--n)); <span class=\"hljs-comment\">\/* the index *\/<\/span>\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>For N=4, we should have 4 indexes (0,1,2,3). The&nbsp;<code>100*var(--_v)\/var(--_m)<\/code>&nbsp;part is a value in the range&nbsp;<code>[0 100]<\/code>&nbsp;and&nbsp;<code>100\/var(--n)<\/code>&nbsp;part is equal to 25. Rounding a value to 25 means it should be a multiplier of 25 so the value will be equal to one of the following: 0, 25, 50, 75, 100. Then if we divide it by 25 we get the indexes.<\/p>\n\n\n\n<p class=\"learn-more\">But we have 5 indexes and not 4.<\/p>\n\n\n\n<p>True, the value 100 alone will create an extra index but we can fix this by clamping the value to the range&nbsp;<code>[0 99]<\/code><\/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\">--_i<\/span>: <span class=\"hljs-selector-tag\">round<\/span>(<span class=\"hljs-selector-tag\">down<\/span>,<span class=\"hljs-selector-tag\">min<\/span>(99,100*<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--_v<\/span>)\/<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--_m<\/span>)),100\/<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--n<\/span>));<\/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>If the progress is equal to 100, we will use 99 because of the&nbsp;<code>min()<\/code>&nbsp;and the round will make it equal to&nbsp;<code>75<\/code>. For the remaining part, check&nbsp;<a href=\"https:\/\/www.smashingmagazine.com\/2023\/07\/define-array-colors-css\/\">my other article<\/a>&nbsp;to understand how I am using a gradient to select a specific color from the array we defined.<\/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-selector-tag\">progress<\/span><span class=\"hljs-selector-attr\">&#91;value]<\/span><span class=\"hljs-selector-pseudo\">::-webkit-progress-value<\/span> {\n   <span class=\"hljs-attribute\">background<\/span>:\n     <span class=\"hljs-built_in\">linear-gradient<\/span>(var(--c)) no-repeat\n     <span class=\"hljs-number\">0<\/span> <span class=\"hljs-built_in\">calc<\/span>(var(--_i)*<span class=\"hljs-built_in\">var<\/span>(--n)*<span class=\"hljs-number\">1%<\/span>\/(<span class=\"hljs-built_in\">var<\/span>(--n) - <span class=\"hljs-number\">1<\/span>))\/<span class=\"hljs-number\">100%<\/span> <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">1px<\/span>*infinity);\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<h3 class=\"wp-block-heading\" id=\"using-an-if-condition\">Using an <code>if()<\/code> condition<\/h3>\n\n\n\n<p>What we have done until now is a conditional logic based on the progress value and CSS has recently introduced&nbsp;<a href=\"https:\/\/css-tricks.com\/if-css-gets-inline-conditionals\/\">inline conditionals using an <code>if()<\/code> syntax<\/a>.<\/p>\n\n\n\n<p>The previous code can be written like below:<\/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-keyword\">@property<\/span> --_i {\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>: 0; \n}\n<span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-attr\">&#91;value]<\/span> {\n  <span class=\"hljs-attribute\">--n<\/span>: <span class=\"hljs-number\">4<\/span>; <span class=\"hljs-comment\">\/* number of ranges *\/<\/span>\n  \n  <span class=\"hljs-attribute\">--_v<\/span>: <span class=\"hljs-built_in\">attr<\/span>(value type(&lt;number&gt;));\n  <span class=\"hljs-attribute\">--_m<\/span>: <span class=\"hljs-built_in\">attr<\/span>(max type(&lt;number&gt;),<span class=\"hljs-number\">1<\/span>);\n  <span class=\"hljs-attribute\">--_i<\/span>: <span class=\"hljs-built_in\">calc<\/span>(var(--n)*<span class=\"hljs-built_in\">round<\/span>(down,min(<span class=\"hljs-number\">99<\/span>,<span class=\"hljs-number\">100<\/span>*var(--_v)\/<span class=\"hljs-built_in\">var<\/span>(--_m)),<span class=\"hljs-number\">100<\/span>\/<span class=\"hljs-built_in\">var<\/span>(--n))\/<span class=\"hljs-number\">100<\/span>); \n}\n<span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-attr\">&#91;value]<\/span><span class=\"hljs-selector-pseudo\">::-webkit-progress-value<\/span> {\n   <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">if<\/span>(\n     style(--_i: <span class=\"hljs-number\">0<\/span>): <span class=\"hljs-number\">#F04155<\/span>;\n     style(<span class=\"hljs-attribute\">--_i<\/span>: <span class=\"hljs-number\">1<\/span>): <span class=\"hljs-number\">#F27435<\/span>;\n     style(<span class=\"hljs-attribute\">--_i<\/span>: <span class=\"hljs-number\">2<\/span>): <span class=\"hljs-number\">#7AB317<\/span>;\n     style(<span class=\"hljs-attribute\">--_i<\/span>: <span class=\"hljs-number\">3<\/span>): <span class=\"hljs-number\">#0D6759<\/span>;\n    );\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<p>The code is self-explanatory and also more intuitive. It\u2019s still too early to adopt this syntax but it\u2019s a good time to know about it.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_MYWLXdW\/b1e70e5cf125f91769b34428f581f2a9\" src=\"\/\/codepen.io\/anon\/embed\/MYWLXdW\/b1e70e5cf125f91769b34428f581f2a9?height=450&amp;theme-id=1&amp;slug-hash=MYWLXdW\/b1e70e5cf125f91769b34428f581f2a9&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed MYWLXdW\/b1e70e5cf125f91769b34428f581f2a9\" title=\"CodePen Embed MYWLXdW\/b1e70e5cf125f91769b34428f581f2a9\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"using-style-queries\">Using Style Queries<\/h3>\n\n\n\n<p>Similar to the <code>if()<\/code> syntax, we can also rely&nbsp;<a href=\"https:\/\/developer.chrome.com\/docs\/css-ui\/style-queries\">on style queries<\/a>&nbsp;and do the following:<\/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-keyword\">@property<\/span> --_i {\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>: 0; \n}\n<span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-attr\">&#91;value]<\/span> {\n  <span class=\"hljs-attribute\">--n<\/span>: <span class=\"hljs-number\">4<\/span>; <span class=\"hljs-comment\">\/* number of ranges *\/<\/span>\n  \n  <span class=\"hljs-attribute\">--_v<\/span>: <span class=\"hljs-built_in\">attr<\/span>(value type(&lt;number&gt;));\n  <span class=\"hljs-attribute\">--_m<\/span>: <span class=\"hljs-built_in\">attr<\/span>(max type(&lt;number&gt;),<span class=\"hljs-number\">1<\/span>);\n  <span class=\"hljs-attribute\">--_i<\/span>: <span class=\"hljs-built_in\">calc<\/span>(var(--n)*<span class=\"hljs-built_in\">round<\/span>(down,min(<span class=\"hljs-number\">99<\/span>,<span class=\"hljs-number\">100<\/span>*var(--_v)\/<span class=\"hljs-built_in\">var<\/span>(--_m)),<span class=\"hljs-number\">100<\/span>\/<span class=\"hljs-built_in\">var<\/span>(--n))\/<span class=\"hljs-number\">100<\/span>); \n}\n<span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-attr\">&#91;value]<\/span><span class=\"hljs-selector-pseudo\">::-webkit-progress-value<\/span> {\n  @container style(<span class=\"hljs-attribute\">--_i<\/span>: <span class=\"hljs-number\">0<\/span>) {background-color: <span class=\"hljs-number\">#F04155<\/span>}\n  <span class=\"hljs-keyword\">@container<\/span> style(--_<span class=\"hljs-attribute\">i:<\/span> <span class=\"hljs-number\">1<\/span>) {<span class=\"hljs-selector-tag\">background-color<\/span>: <span class=\"hljs-selector-id\">#F27435<\/span>}\n  <span class=\"hljs-keyword\">@container<\/span> style(--_<span class=\"hljs-attribute\">i:<\/span> <span class=\"hljs-number\">2<\/span>) {<span class=\"hljs-selector-tag\">background-color<\/span>: <span class=\"hljs-selector-id\">#7AB317<\/span>}\n  <span class=\"hljs-keyword\">@container<\/span> style(--_<span class=\"hljs-attribute\">i:<\/span> <span class=\"hljs-number\">3<\/span>) {<span class=\"hljs-selector-tag\">background-color<\/span>: <span class=\"hljs-selector-id\">#0D6759<\/span>}\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<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_ZYEwjXp\/80db141cbe48270f0bfb511e9f354162\" src=\"\/\/codepen.io\/anon\/embed\/ZYEwjXp\/80db141cbe48270f0bfb511e9f354162?height=450&amp;theme-id=1&amp;slug-hash=ZYEwjXp\/80db141cbe48270f0bfb511e9f354162&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed ZYEwjXp\/80db141cbe48270f0bfb511e9f354162\" title=\"CodePen Embed ZYEwjXp\/80db141cbe48270f0bfb511e9f354162\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>We will also be able to have&nbsp;<a href=\"https:\/\/github.com\/w3c\/csswg-drafts\/issues\/8376\">a range syntax<\/a>&nbsp;and the code can be simplified to something like the below:<\/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> --_i {\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>: 0; \n}\n<span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-attr\">&#91;value]<\/span> {\n  <span class=\"hljs-attribute\">--_v<\/span>: <span class=\"hljs-built_in\">attr<\/span>(value type(&lt;number&gt;));\n  <span class=\"hljs-attribute\">--_m<\/span>: <span class=\"hljs-built_in\">attr<\/span>(max type(&lt;number&gt;),<span class=\"hljs-number\">1<\/span>);\n  <span class=\"hljs-attribute\">--_i<\/span>: <span class=\"hljs-built_in\">calc<\/span>(var(--_v)\/<span class=\"hljs-built_in\">var<\/span>(--_m)); \n}\n<span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-attr\">&#91;value]<\/span><span class=\"hljs-selector-pseudo\">::-webkit-progress-value<\/span> {\n  <span class=\"hljs-attribute\">background-color<\/span>: <span class=\"hljs-number\">#0D6759<\/span>;\n  @container style(--_i &lt; .75) {<span class=\"hljs-attribute\">background-color<\/span>: <span class=\"hljs-number\">#7AB317<\/span>}\n  <span class=\"hljs-keyword\">@container<\/span> style(--_i &lt; .<span class=\"hljs-number\">5<\/span> ) {<span class=\"hljs-selector-tag\">background-color<\/span>: <span class=\"hljs-selector-id\">#F27435<\/span>}\n  <span class=\"hljs-keyword\">@container<\/span> style(--_i &lt; .<span class=\"hljs-number\">25<\/span>) {<span class=\"hljs-selector-tag\">background-color<\/span>: <span class=\"hljs-selector-id\">#F04155<\/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<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_bNpVNjz\" src=\"\/\/codepen.io\/anon\/embed\/bNpVNjz?height=450&amp;theme-id=1&amp;slug-hash=bNpVNjz&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed bNpVNjz\" title=\"CodePen Embed bNpVNjz\" 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 this article and the previous one give you a good overview of what modern CSS looks like. We are far from the era of simply setting&nbsp;<code>color: red<\/code>&nbsp;and&nbsp;<code>margin: auto<\/code>. Now, it\u2019s a lot of variables, calculations, conditional logic, and more!<\/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>Now that we&#8217;re starting to be able to apply types (like `number`) to values of attributes we pull of HTML elements in CSS, doing interesting things with <progress> is easier.<\/p>\n","protected":false},"author":12,"featured_media":5567,"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":[300,7,32],"class_list":["post-5537","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-attr","tag-css","tag-progress"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/Custom-progress-element-using-the-attr-function.jpg?fit=1140%2C676&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/5537","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=5537"}],"version-history":[{"count":12,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/5537\/revisions"}],"predecessor-version":[{"id":7760,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/5537\/revisions\/7760"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/5567"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=5537"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=5537"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=5537"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}