Primitives Feedback
Meter
The native meter element: a measurement within a known range. The browser decides from low, high and optimum whether the value is optimum, suboptimum or even less good; the stylesheet colors each region from the status tokens. Tier 0.
- Tier
- 0 · Native
- Behavior JS
- 0 B brotli · 0 B gzip ·
fixtures/· methodresults.json (native-only) - Platform features
<meter>,low,high,optimum,::-webkit-meter-optimum-value- Shared listeners
- none
- Per-instance listeners
- none
- Lazy state
- none
- Native base
<meter min max low high optimum value>
On this page
Example
<label for="meter-photos">Photos: 42 GB of 100 GB</label>
<meter id="meter-photos" data-slean="meter" min="0" max="100" low="70" high="90" optimum="0" value="42">42 GB</meter>
<label for="meter-mail">Mail: 78 GB of 100 GB</label>
<meter id="meter-mail" data-slean="meter" min="0" max="100" low="70" high="90" optimum="0" value="78">78 GB</meter>
<label for="meter-backups">Backups: 96 GB of 100 GB</label>
<meter id="meter-backups" data-slean="meter" min="0" max="100" low="70" high="90" optimum="0" value="96">96 GB</meter><script lang="ts">
// Nothing to import at runtime: the browser owns the meter. The stylesheet is optional.
import '@svelte-lean/styles/meter.css';
</script>
<label for="meter-photos">Photos: 42 GB of 100 GB</label>
<meter id="meter-photos" data-slean="meter" min="0" max="100" low="70" high="90" optimum="0" value="42">42 GB</meter>
<label for="meter-mail">Mail: 78 GB of 100 GB</label>
<meter id="meter-mail" data-slean="meter" min="0" max="100" low="70" high="90" optimum="0" value="78">78 GB</meter>
<label for="meter-backups">Backups: 96 GB of 100 GB</label>
<meter id="meter-backups" data-slean="meter" min="0" max="100" low="70" high="90" optimum="0" value="96">96 GB</meter>Why this implementation exists
A meter is a measurement with a judgement: storage that is fine, tight or full. The platform already makes the judgement: low, high and optimum split the range, and the browser decides which region the value falls in, with no script and no comparison in the template.
What the platform leaves to authors is the look. meter.css sets appearance: none, draws the track, and gives each region a status token through the vendor pseudo-elements, so the color follows the browser’s decision.
The browser owns
- the value and the range
- placing the value in the optimum, suboptimum or even-less-good region
- the meter role with its name and value
- the direction of the fill under RTL
Svelte Lean owns
- meter.css: the track, a status token per region, the accent when there are no regions
- the sizes
- the contract
- documentation
Usage
The markup needs no package. Install @svelte-lean/styles for the stylesheet; @svelte-lean/primitives adds the typed contract and nothing at runtime.
npm install @svelte-lean/stylespnpm add @svelte-lean/stylesyarn add @svelte-lean/stylesbun add @svelte-lean/stylesimport '@svelte-lean/styles/meter.css';Set min, max and value; add low, high and optimum when some values are better than others. A low optimum makes high values bad (storage), a high one makes low values bad (a score).
Say the value in text as well, in the label or next to the meter: the color alone is not read, and it is lost under forced colors.
Anatomy
| Part | Element | data-slean-part | Required | Notes |
|---|---|---|---|---|
| root | <meter data-slean="meter" min max low high optimum value> | – | yes | Without low and high the value has one region and takes the accent. data-size="sm|lg". A label through <label for>, aria-labelledby or aria-label. |
Runtime profile
Tier 0: the meter has no behavior module, the Vite plugin maps meter to no module, and the page ships no Svelte Lean JavaScript for it.
Accessibility contract
- The browser exposes the
meterrole with the value; a name comes from<label for>,aria-labelledbyoraria-label. aria-valuetextgives the value a text form when the number alone says little (a strength word for a score).- The region is shown by color only; the value in text carries the meaning for everyone.
- A meter is not a progress indicator: a task that completes is a progress.
Keyboard
| Key | When | Result |
|---|---|---|
| Tab | anywhere | Skips the meter: it is not focusable (native) |
Platform features
| Feature | Baseline | Outside the target |
|---|---|---|
<meter> with low, high, optimum | Widely available | Not applicable |
appearance: none | Baseline 2022 | The platform’s own gauge |
::-webkit-meter-*-value, ::-moz-meter-bar | Not standardized; Chromium and WebKit use the first, Firefox the second | Not applicable |
Without JavaScript
Fully functional: the rendered value and its region are visible.
Server rendering
The attributes render the value and the thresholds; the browser computes the region on the first paint.
Before hydration
Nothing is attached. The meter rendered on the server is the element the application updates after hydration.
Styling
meter.css makes the element the track, gives the inner pseudo-elements the element’s block size (a percentage height does not fill it in Chromium), and colors the optimum, suboptimum and even-less-good values with the success, warning and danger tokens; a meter without regions takes the accent. Under forced colors the track gets a border and every region is Highlight. The tokens it reads:
| Token | Default (light) | Applies to |
|---|---|---|
--slean-radius-full | 9999px | border-radius |
--slean-muted | var(--slean-neutral-3) | background |
--slean-success | oklch(55% 0.15 150) | background |
--slean-warning | oklch(76% 0.16 80) | background |
--slean-danger | oklch(55% 0.2 25) | background |
--slean-accent | oklch(54% 0.19 258) | background |
State selectors the stylesheet targets, all from the platform or ARIA: :-moz-meter-sub-optimum, :-moz-meter-sub-sub-optimum, ::-moz-meter-bar, ::-webkit-meter-bar, ::-webkit-meter-even-less-good-value, ::-webkit-meter-inner-element, ::-webkit-meter-optimum-value, ::-webkit-meter-suboptimum-value, [high], [low].
Variant attributes: data-size (sm, lg).
Controlled integration
The application owns the value. A password score from 0 to 4 has its optimum at the top, so a low score is even less good; aria-valuetext reads the word instead of the number.
<script lang="ts">
// A password score from 0 to 4: the optimum is high, so a low score is even less good.
let { score }: { score: number } = $props();
const words = ['Very weak', 'Weak', 'Fair', 'Good', 'Strong'];
</script>
<label for="strength">Strength: {words[score]}</label>
<meter
id="strength"
data-slean="meter"
min="0"
max="4"
low="2"
high="3"
optimum="4"
value={score}
aria-valuetext={words[score]}
></meter>Compatibility notes
The element and its thresholds are widely available; appearance: none is Baseline 2022. The region pseudo-elements are not standardized: Chromium and WebKit draw the ::-webkit-meter-*-value elements, Firefox draws ::-moz-meter-bar and exposes the region as a pseudo-class.
Examples
One region
Without low and high every value is in the optimum region, so there is nothing
good or bad to show: the stylesheet draws the value in the accent.
<!-- No low and no high: one region, drawn in the accent. -->
<label for="profile">Profile complete: 60%</label>
<meter id="profile" data-slean="meter" value="0.6">60%</meter>Testing
apps/playground/tests/primitives/feedback-navigation.spec.tsPlaywright: roles, names and states in the accessibility tree, keyboard, RTL, reduced motion, axepackages/styles/testsstatic checks of the stylesheet (layers, tokens, specificity, dark parity)
Source
packages/primitives/src/meter/contract.mdthe contract in prose: semantics, keyboard, focus, ARIA, SSR, JS-disabled behaviorpackages/primitives/src/meter/contract.tstyped constants: name, tier, base, parts, options, eventspackages/styles/css/meter.cssthe optional stylesheet