API
Progress
The native `<progress>`, determinate or indeterminate.
Installation
pnpm dlx zazz-ui add progressnpx zazz-ui add progressyarn dlx zazz-ui add progressbunx zazz-ui add progressRun zazz-ui init first if the project has no zazz/ folder yet.
Examples
Default
<div data-ui="field">
<label
style="
--display: flex;
--items: center;
--justify: space-between;
"
data-field-slot="label"
for="f-upload"
>
<span>Reticulating splines…</span>
<span
style="
--font-size: var(--font-size-xs);
--text: var(--color-muted-foreground);
"
>62%</span
>
</label>
<progress data-ui="progress" id="f-upload" max="100" value="62">62%</progress>
</div>/**
* progress.css — Progress ([data-ui~="progress"] on native <progress>)
*
* @layer variables, reset, components
* @requires _layers.css, _variables.css, _reset.css, _properties.css (--_*-resolved)
* @uses appearance: none — element box becomes the track; only the fill
* needs vendor pseudo-elements (::-webkit-progress-value,
* ::-moz-progress-bar) — never combine the two vendors in one
* selector list (an unknown pseudo invalidates the whole rule)
* @uses :indeterminate — animated stripe on the element background
* @tokens --ui-progress-* (@layer variables)
* @consumedby meter.css
*/
@layer variables {
:root {
--ui-progress-w: 100%;
--ui-progress-h: calc(var(--spacing) * 1.5);
--ui-progress-rounded: var(--radius-full);
--ui-progress-bg: var(--color-muted);
--ui-progress-track-border: 1px solid transparent;
--ui-progress-bar-bg: var(--color-primary);
--ui-progress-indeterminate-duration: 1s;
}
}
@layer reset {
/* ===========================================================================
PROGRESS — native <progress> fill redraw
The element box is the track (overflow clips the fill's corners); only the
fill pseudos are redrawn, one rule per vendor.
=========================================================================== */
:where([data-ui~="progress"]) {
--_w-len: var(--_w-resolved, var(--ui-progress-w));
--_w-num: var(--_w-resolved, var(--ui-progress-w));
inline-size: calc(var(--_w-len) + var(--_w-num) * var(--spacing));
--_h-len: var(--_h-resolved, var(--ui-progress-h));
--_h-num: var(--_h-resolved, var(--ui-progress-h));
block-size: calc(var(--_h-len) + var(--_h-num) * var(--spacing));
overflow: hidden;
background-color: var(--_bg-resolved, var(--ui-progress-bg));
border: var(--ui-progress-track-border);
border-radius: var(--_rounded-resolved, var(--ui-progress-rounded));
}
:where([data-ui~="progress"])::-webkit-progress-bar {
background: transparent;
}
:where([data-ui~="progress"])::-webkit-progress-value {
background-color: var(--ui-progress-bar-bg);
border-radius: inherit;
transition: inline-size var(--default-transition-duration)
var(--default-transition-timing-function);
}
:where([data-ui~="progress"])::-moz-progress-bar {
background-color: var(--ui-progress-bar-bg);
border-radius: inherit;
}
/* Stripe on the element itself so it renders the same in every engine; the
vendor fills go transparent to avoid a phantom bar. Percentage stops in a
square tile wrap seamlessly at 45°, and animating exactly one tile keeps
the loop jump-free; the padding-box clip keeps stripes out of the border. */
:where([data-ui~="progress"]:indeterminate) {
--_stripe: color-mix(in oklch, var(--ui-progress-bar-bg) 30%, transparent);
background-image: linear-gradient(
-45deg,
var(--_stripe) 25%,
transparent 25% 50%,
var(--_stripe) 50% 75%,
transparent 75%
);
background-size: calc(var(--spacing) * 4) calc(var(--spacing) * 4);
background-origin: padding-box;
background-clip: padding-box;
}
:where([data-ui~="progress"]:indeterminate)::-webkit-progress-value {
background: transparent;
}
:where([data-ui~="progress"]:indeterminate)::-moz-progress-bar {
background: transparent;
}
@media (prefers-reduced-motion: no-preference) {
:where([data-ui~="progress"]:indeterminate) {
animation: ui-progress-slide var(--ui-progress-indeterminate-duration) linear infinite;
}
}
@keyframes ui-progress-slide {
from {
background-position: 0 0;
}
to {
background-position: calc(var(--spacing) * 4) 0;
}
}
}Indeterminate
<progress data-ui="progress" aria-label="Loading"></progress>/**
* progress.css — Progress ([data-ui~="progress"] on native <progress>)
*
* @layer variables, reset, components
* @requires _layers.css, _variables.css, _reset.css, _properties.css (--_*-resolved)
* @uses appearance: none — element box becomes the track; only the fill
* needs vendor pseudo-elements (::-webkit-progress-value,
* ::-moz-progress-bar) — never combine the two vendors in one
* selector list (an unknown pseudo invalidates the whole rule)
* @uses :indeterminate — animated stripe on the element background
* @tokens --ui-progress-* (@layer variables)
* @consumedby meter.css
*/
@layer variables {
:root {
--ui-progress-w: 100%;
--ui-progress-h: calc(var(--spacing) * 1.5);
--ui-progress-rounded: var(--radius-full);
--ui-progress-bg: var(--color-muted);
--ui-progress-track-border: 1px solid transparent;
--ui-progress-bar-bg: var(--color-primary);
--ui-progress-indeterminate-duration: 1s;
}
}
@layer reset {
/* ===========================================================================
PROGRESS — native <progress> fill redraw
The element box is the track (overflow clips the fill's corners); only the
fill pseudos are redrawn, one rule per vendor.
=========================================================================== */
:where([data-ui~="progress"]) {
--_w-len: var(--_w-resolved, var(--ui-progress-w));
--_w-num: var(--_w-resolved, var(--ui-progress-w));
inline-size: calc(var(--_w-len) + var(--_w-num) * var(--spacing));
--_h-len: var(--_h-resolved, var(--ui-progress-h));
--_h-num: var(--_h-resolved, var(--ui-progress-h));
block-size: calc(var(--_h-len) + var(--_h-num) * var(--spacing));
overflow: hidden;
background-color: var(--_bg-resolved, var(--ui-progress-bg));
border: var(--ui-progress-track-border);
border-radius: var(--_rounded-resolved, var(--ui-progress-rounded));
}
:where([data-ui~="progress"])::-webkit-progress-bar {
background: transparent;
}
:where([data-ui~="progress"])::-webkit-progress-value {
background-color: var(--ui-progress-bar-bg);
border-radius: inherit;
transition: inline-size var(--default-transition-duration)
var(--default-transition-timing-function);
}
:where([data-ui~="progress"])::-moz-progress-bar {
background-color: var(--ui-progress-bar-bg);
border-radius: inherit;
}
/* Stripe on the element itself so it renders the same in every engine; the
vendor fills go transparent to avoid a phantom bar. Percentage stops in a
square tile wrap seamlessly at 45°, and animating exactly one tile keeps
the loop jump-free; the padding-box clip keeps stripes out of the border. */
:where([data-ui~="progress"]:indeterminate) {
--_stripe: color-mix(in oklch, var(--ui-progress-bar-bg) 30%, transparent);
background-image: linear-gradient(
-45deg,
var(--_stripe) 25%,
transparent 25% 50%,
var(--_stripe) 50% 75%,
transparent 75%
);
background-size: calc(var(--spacing) * 4) calc(var(--spacing) * 4);
background-origin: padding-box;
background-clip: padding-box;
}
:where([data-ui~="progress"]:indeterminate)::-webkit-progress-value {
background: transparent;
}
:where([data-ui~="progress"]:indeterminate)::-moz-progress-bar {
background: transparent;
}
@media (prefers-reduced-motion: no-preference) {
:where([data-ui~="progress"]:indeterminate) {
animation: ui-progress-slide var(--ui-progress-indeterminate-duration) linear infinite;
}
}
@keyframes ui-progress-slide {
from {
background-position: 0 0;
}
to {
background-position: calc(var(--spacing) * 4) 0;
}
}
}Attributes
| Attribute | Kind | Values | Notes |
|---|---|---|---|
data-ui="progress" | Identity | free value | Identity token in data-ui. |
Hooks
Set a hook on :root, on a subtree, or inline on one element. A state hook (--…--hover) themes that state only; the primitive keeps its other states.
| Hook | Default | State |
|---|---|---|
--ui-progress-bar-bg | var(--color-primary) | — |
--ui-progress-bg | var(--color-muted) | — |
--ui-progress-h | calc(var(--spacing) * 1.5) | — |
--ui-progress-indeterminate-duration | 1s | — |
--ui-progress-rounded | var(--radius-full) | — |
--ui-progress-track-border | 1px solid transparent | — |
--ui-progress-w | 100% | — |