sveltelean Primitives
Versionv0.2.0 GitHub

Example

Three regions of one range
42 GB 78 GB 96 GB
<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>
Tier 0: the two sources differ only by the stylesheet import. The three meters share one range (low 70, high 90, optimum 0); the browser puts each value in its region and the stylesheet colors it. Everything here works with page JavaScript disabled.

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/styles
stylesheets
import '@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

PartElementdata-slean-partRequiredNotes
root<meter data-slean="meter" min max low high optimum value>–yesWithout 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 meter role with the value; a name comes from <label for>, aria-labelledby or aria-label.
  • aria-valuetext gives 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

KeyWhenResult
TabanywhereSkips the meter: it is not focusable (native)

Platform features

FeatureBaselineOutside the target
<meter> with low, high, optimumWidely availableNot applicable
appearance: noneBaseline 2022The platform’s own gauge
::-webkit-meter-*-value, ::-moz-meter-barNot standardized; Chromium and WebKit use the first, Firefox the secondNot 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:

TokenDefault (light)Applies to
--slean-radius-full9999pxborder-radius
--slean-mutedvar(--slean-neutral-3)background
--slean-successoklch(55% 0.15 150)background
--slean-warningoklch(76% 0.16 80)background
--slean-dangeroklch(55% 0.2 25)background
--slean-accentoklch(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.

strength.svelte
<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.

60%
one region
<!-- 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

Source