progress()
Experimental: This is an experimental technology
Check the Browser compatibility table carefully before using this in production.
The progress()
CSS function returns a <number>
value representing the position of one value (the progress value) relative to two other values (the progress start and end values).
Syntax
/* With fixed progress value */
progress(300, 0, 1000)
progress(50px, 0px, 100px)
progress(50%, 30%, 80%)
/* With custom property */
progress(var(--container-width), 320, 1200)
/* Inside math function */
calc((progress(var(--container-width), 20%, 80%) / 2) + 0.5)
/* Inside non-math function */
rgb(
calc(255 * progress(var(--container-width), 320px, 1200px))
calc(255 * progress(var(--container-width), 320px, 1200px)) 255 / 0.5
);
/* Math function inside progress() */
progress(calc(20 + 30), 0, 100)
Parameters
The progress()
function takes three comma-separated <calc-sum>
expressions as parameters:
progress(<calc-sum>, <calc-sum>, <calc-sum>)
Respectively, these are:
- Progress
-
The value to calculate the position of relative to the other two values.
- Progress start
-
The lower progress bound.
- Progress end
-
The upper progress bound.
Return value
A <number>
representing the position of the progress value relative to the other two values. This is calculated as follows:
(progress - progress start) / (progress end - progress start)
If the progress value is in between the progress start and progress end values, the return value will between 0
and 1
, representing a percentage. If the progress value is smaller than the progress start value, or larger than the progress end value, the function is still valid, but the return value is clamped to 0
or 1
, respectively.
Description
The CSS progress()
function provides a way to calculate a progress ratio, which is useful for creating use cases such as progress bar animations, or boxes that fade in as they become wider to reveal their contents.
The simplest possible use might look like this:
opacity: progress(5, 0, 10);
In this case, the computed value of opacity
would be 0.5
, as 5 is mid-way between 0
and 10
.
Permitted unit types
The parameters of a progress()
function can be mathematical expressions or simple values. The values (or expression results) can be any <number>
, <dimension>
, or <percentage>
value. They can have different units, but they must all be of the same type, or else the function is invalid.
The example we saw previously is valid — all of its parameters are unitless <number>
values:
progress(5, 0, 10)
The next example is also valid — all of its parameters have <length>
units. Behind the scenes, the computed values are used for the calculation. Provided the font-size
is 16px
at the point of calculation, 3em
will resolve to 48px
, which is 48%
of the way between 0px
and 100px
, so the return value will be 0.48
.
progress(3em, 0px, 100px)
The final couple of examples in this section is not valid, however. The types do not match, so the resulting calculations do not make sense.
progress(3s, 0px, 100px)
progress(3em, 0, 100)
Creating unitless values
The progress()
function outputs unitless values, therefore it can be used for the purpose of removing units from values in the same manner as the tan(atan2())
hack. Note however that, due to the updates in behavior around CSS typed arithmetic, this can also be achieved via simple division.
Combining progress()
with other functions and custom properties
Because progress()
only ever returns a unitless value between 0
and 1
, it is common to combine it with another math function such as calc()
to output the value and units you want. You can also use CSS custom properties inside progress()
functions — this makes sense, as you'll often want to set the same values in multiple places, and/or base them on custom properties set via JavaScript.
The following example calculates what percentage the viewport width is between a minimum width of 320px
and a maximum width of 1200px
. The calc()
function is used to multiply the progress()
return value by 600px
to convert it into a pixel value that will half of the viewport width's progress value between 320px
and 1200px
.
width: calc(progress(100vw, 320px, 1200px) * 600px);
For example, if the viewport width is 700px
, the progress value will be calculated as ((700 - 320) / (1200 - 320))
= 0.431818
. The width will then be calculated as 0.431818 * 600px
, which equals 259.1px
.
The next example is an update of the previous one, in which we've used custom properties for the progress, progress start, and progress end values.
width: calc(
progress(
var(--container-width),
var(--custom-minwidth),
var(--custom-maxwidth)
) *
var(--custom-maxwidth)
);
It is possible to use progress()
functions to calculate individual values inside other functions and component values inside shorthand property values, provided your functions return valid types for those values.
This can result in some complex expressions. For example, here we are calculating the first two channels of an rgb()
color proportionate to the same width proportion as before:
background-color: rgb(
calc(
255 *
progress(
var(--container-width),
var(--custom-minwidth),
var(--custom-maxwidth)
)
)
calc(
255 *
progress(
var(--container-width),
var(--custom-minwidth),
var(--custom-maxwidth)
)
)
255 / 0.5
);
Formal syntax
<progress()> =
progress( <calc-sum> , <calc-sum> , <calc-sum> )
<calc-sum> =
<calc-product> [ [ '+' | '-' ] <calc-product> ]*
<calc-product> =
<calc-value> [ [ '*' | / ] <calc-value> ]*
<calc-value> =
<number> |
<dimension> |
<percentage> |
<calc-keyword> |
( <calc-sum> )
<calc-keyword> =
e |
pi |
infinity |
-infinity |
NaN
Examples
>Basic progress()
usage
In this example, we'll show basic usage of the progress()
function to set a progress bar width
as a percentage equal to the progress ratio of its parent's width
between its min-width
and max-width
.
HTML
Our HTML features a <section>
element representing our content, and a <div>
element representing the width progress bar.
<section>
<div class="progress"></div>
</section>
CSS
In our CSS, we first set some custom properties on our <section>
element to represent its min-width
, max-width
, and width
. We then set those properties to the appropriate custom property values, then give our <section>
a solid background-color
so it is visible.
section {
--custom-minwidth: 300px;
--custom-maxwidth: 700px;
--custom-width: 600px;
min-width: var(--custom-minwidth);
max-width: var(--custom-maxwidth);
width: var(--custom-width);
background-color: cyan;
}
Now onto our <div>
— we first give it a height
and a dark background-color
so it stands out against our <section>
element. We then calculate its width
by using a progress()
function to calculate the progress ratio of the width between the minimum and maximum width, then using a calc()
function to multiply the progress()
return value by 100%
to return a percentage.
.progress {
height: 4px;
background-color: red;
width: calc(
progress(
var(--custom-width),
var(--custom-minwidth),
var(--custom-maxwidth)
) *
100%
);
}
Result
This demo will render as follows:
The width of the <div>
is 75%
of the <section>
width, as the min-width
is 400px
, the max-width
is 700px
, and the width
is 600px
, which is 75%
of the distance between the previous two values.
Resize effects on a container
This example shows off some more involved uses of the progress()
function, resulting in some fun effects as the browser window is resized.
This example works much better when rendered full-size in a desktop browser tab. Therefore, we have not rendered it in an embedded live sample in this page. Instead, you can find it running live at CSS progress()
function demo (also see the source code).
Open the live example in a separate tab, and try increasing and decreasing the browser window width to see the effect. Keep this open so you can refer back to it as you read the explanation below.
HTML
Our HTML features an <article>
element that contains the rest of our content, and two <section>
elements — one to hang a background image off, and the other one to contain our content. The <section class="content">
also contains a <div class="progress">
representing a width progress bar, the same as the one in our previous demo. We have omitted the rest of the content for brevity.
<article>
<section class="background"></section>
<section class="content">
<div class="progress"></div>
<!-- Content here -->
</section>
</article>
JavaScript
In our script, we first grab a reference to our <article>
element. We then define a function called setContainerWidth()
, which grabs the <article>
's client width via Element.getBoundingClientRect()
, and sets a custom property on it called --container-width
, which is equal to the client width rounded down, with px
appended to it.
We then set a resize
event listener on the Window
object, which runs setContainerWidth()
when the browser window is resized. We also run it once to set the --container-width
custom property on the <article>
element once the page loads.
const articleElem = document.querySelector("article");
function setContainerWidth() {
const clientWidth = articleElem.getBoundingClientRect().width;
articleElem.style.setProperty(
"--container-width",
`${Math.floor(clientWidth)}px`,
);
}
window.addEventListener("resize", setContainerWidth);
setContainerWidth();
With this set up, we can now set some property values based on the --container-width
, so that parts of our design will dynamically change as the window resizes.
CSS
The following section explains just the CSS relevant to how we've used the progress()
function in the demo. For the full CSS, see the CSS source.
We first center the <article>
inside the <body>
using flexbox, then set some custom properties on it to represent the min-width
and max-width
values we will use elsewhere. We then target the <article>
element, giving it min-width
and max-width
values equal to the custom properties we set earlier. We set its position
to relative
so we can position its content relative to it, then give it a percentage width
, fixed height
, and border
.
body {
height: inherit;
display: flex;
justify-content: center;
align-items: center;
--custom-minwidth: 320px;
--custom-maxwidth: 1200px;
}
article {
min-width: var(--custom-minwidth);
max-width: var(--custom-maxwidth);
position: relative;
width: 70%;
height: 600px;
border: 3px solid black;
}
Now on to our progress
<div>
. We set its width
equal to a percentage based on the progress ratio of the --container-width
custom property set on the <article>
element via JavaScript between its min-width
and max-width
(we use the same custom properties here for the second and third progress()
parameters as we did for the <article>
min-width
and max-width
).
We also give it a height
and background-color
, then absolutely position it at the top and left corner of the <article>
.
.progress {
width: calc(
progress(
var(--container-width),
var(--custom-minwidth),
var(--custom-maxwidth)
) *
100%
);
height: 4px;
background-color: red;
position: absolute;
top: 0;
left: 0;
}
Next, we'll look at our background
<section>
. We absolutely position it relative to our <article>
, setting inset: 0
on it so that it adopts the same size and sits over the top of it. We then set a rather wide background-image
on it, and position the background image by giving the background-position-x
property the same value as we gave the progress bar's width
property. This has the effect that, when you increase the browser window width, the background image is moved over to the left, creating a nice image scrolling effect.
.background {
position: absolute;
inset: 0;
background-image: url(https://mdn.github.io/shared-assets/images/examples/wide-background.jpg);
background-position-x: calc(
progress(
var(--container-width),
var(--custom-minwidth),
var(--custom-maxwidth)
) *
100%
);
}
We position the content
<section>
absolutely so it sits over the top of the background
<section>
, then give it some padding
. We then vary two property values as the browser window is resized, using the same progress ratio as before:
- We vary the R and G components of the
background-color
, multiplying the progress ratio by 255 in each case to get a proportionate channel value. As the window gets wider, the background color gets less blue and more white, making the scene look as it is going from night to day (the color value has an opacity of0.5
, so it acts like a tint for the underlying image). - We vary the
opacity
so that the content fades in a bit when the window gets wider.
.content {
position: absolute;
inset: 0;
padding: 20px;
background-color: rgb(
calc(
255 *
progress(
var(--container-width),
var(--custom-minwidth),
var(--custom-maxwidth)
)
)
calc(
255 *
progress(
var(--container-width),
var(--custom-minwidth),
var(--custom-maxwidth)
)
)
255 / 0.5
);
opacity: calc(
(
progress(
var(--container-width),
var(--custom-minwidth),
var(--custom-maxwidth)
) /
2
) +
0.5
);
}
Specifications
Specification |
---|
CSS Values and Units Module Level 5> # progress> |
Browser compatibility
Loading…