Skip to main content
astrocraft-ui/ components · 101

Meter

Free · MIT

Headless, accessible, and styled entirely through the attribute surface below — bring your own CSS system, or start from the reference theme.

Install

Without a flag you get no styling at all — plain HTML carrying the attribute surface below. --theme copies the same theme in two dialects;--bridge lumos re-points either one onto Lumos for Astro's tokens (it needs a theme to re-point, and Lumos itself, already in your project).

Unstyled — behavior only, you write the CSS

Install command
npx astrocraft-ui add display/meter

Plain-CSS theme — no build step

Install command
npx astrocraft-ui add display/meter --theme css

Tailwind theme — needs Tailwind v4

Install command
npx astrocraft-ui add display/meter --theme tailwind

Plain-CSS theme, re-pointed onto Lumos tokens

Install command
npx astrocraft-ui add display/meter --theme css --bridge lumos

Tailwind theme, re-pointed onto Lumos tokens

Install command
npx astrocraft-ui add display/meter --theme tailwind --bridge lumos

Live demo

CircularProgress & Meter

The ring is one dash the length of its own circumference, slid by stroke-dashoffset — no path maths, no clipping element. The meter is the native <meter>: a MEASUREMENT, not a task, and the only element that can say which end of its range is the good one. It ships with no appearance: none, for the same reason the Slider does — zeroing it erases the control in WebKit.

Storage — 7.2 GB of 10 GB7.2 / 10
Score — 8.4 of 108.4 / 10

Attribute surface

With no class-merging machinery, these attributes are the whole extension point. State is a data-attribute or a native attribute; visibility is the hiddenattribute. Generated from source by pnpm slots.

ComponentSlotsVariantsRuntime stateNative state
`Meter.astro``meter``data-size`: `sm` · `md` · `lg`——

Source

What the command copies — 2 files, alias-free so the copy lands standing alone. This is the whole component; there is nothing else.

src/components/ui/display/meter/Meter.astro
---
// src/components/ui/display/meter/Meter.astro — headless primitive (see ../../README.md).
// `<meter>`: the native element nobody remembers exists. Disk usage, storage quota, a score out of
// ten, password strength, a battery — a MEASUREMENT within a known range.
//
// It is not a Progress bar, and the difference is not cosmetic. `<progress>` (and our Progress) is a
// TASK: it starts at nothing, ends at done, and only ever goes up. A meter is a reading: it can go
// down, "full" may be bad news, and it carries something no progress bar can express —
// `low` / `high` / `optimum`, which tell the browser and assistive tech which end of the range is
// the good one. That is why "7.2 GB of 10 GB used" is a meter and a file upload is not.
//
//   <Meter value={7.2} max={10} high={8} optimum={0} label="Storage used" />
//
// Two things are deliberately NOT here:
//
//   • No `appearance: none`. That is the Slider lesson exactly — zeroing the appearance of a native
//     control erases the UA's whole rendering, so shipping it without geometry would turn "unstyled"
//     into "invisible". Restyling a meter is all-or-nothing and belongs to a theme that means it;
//     `theme-default.css` has the worked example. Left alone, you get the browser's control, which
//     works everywhere and needs no CSS at all.
//   • No fallback bar built from divs. The slot IS the fallback: its contents render only in a
//     browser with no <meter>, which in practice means a text version of the same number.
import type { HTMLAttributes } from "astro/types";

type Props = HTMLAttributes<"meter"> & {
  value: number;
  min?: number;
  max?: number;
  /** The top of the "low" band — below this the browser may render the meter as poor. */
  low?: number;
  /** The bottom of the "high" band. */
  high?: number;
  /** Where in the range is GOOD. Put it below `low` when a high reading is the bad news. */
  optimum?: number;
  /** The reading's accessible name. A meter with no name is announced as an unlabelled one. */
  label?: string;
  size?: "sm" | "md" | "lg";
};

const {
  value,
  min = 0,
  max = 100,
  low,
  high,
  optimum,
  label,
  size = "md",
  class: className,
  ...rest
} = Astro.props;
---

<meter
  value={value}
  min={min}
  max={max}
  low={low}
  high={high}
  optimum={optimum}
  aria-label={label}
  class={className}
  data-slot="meter"
  data-size={size}
  {...rest}><slot>{value} / {max}</slot></meter
>
src/components/ui/display/meter/index.ts
import Meter from "./Meter.astro";

export { Meter };
export default Meter;

What you get

The component source, copied into your project by npx astrocraft-ui add display/meter — no package dependency, no CSS to fight. Whatever the paid blocks compose, this is it.