Skip to content

cv-meter

Graphical display of a numeric value within a known range, such as disk usage or password strength.

Headless: createMeter

Usage

View source
html
<div class="meter-demo-shell" data-demo="meter" data-theme="dark" data-live-demo-height="780">
  <section class="meter-demo-hero" aria-labelledby="meter-demo-title">
    <div class="meter-demo-copy">
      <span class="meter-demo-kicker">Known range measurement</span>
      <h3 id="meter-demo-title">Measure a condition. Do not imply a task is running.</h3>
      <p>
        Use <code>cv-meter</code> for capacity, quality, pressure, strength, or saturation values that already
        exist inside a bounded range. The component owns meter semantics and threshold status; the caller owns
        the measured value.
      </p>
    </div>

    <dl class="meter-demo-metrics" aria-label="Meter contract summary">
      <div>
        <dt>Semantics</dt>
        <dd>role="meter"</dd>
      </div>
      <div>
        <dt>Status zones</dt>
        <dd>low / optimum / high</dd>
      </div>
      <div>
        <dt>No fallback</dt>
        <dd>not indeterminate</dd>
      </div>
    </dl>
  </section>

  <section class="meter-demo-workbench" aria-labelledby="meter-demo-workbench-title">
    <div class="meter-demo-panel">
      <header class="meter-demo-panel-head">
        <div>
          <span class="meter-demo-kicker">Storage pressure</span>
          <h4 id="meter-demo-workbench-title">remote-vault-01</h4>
        </div>
        <cv-badge variant="warning">Measured</cv-badge>
      </header>

      <div class="meter-demo-primary-reading">
        <div class="meter-demo-reading-label">
          <span>Capacity used</span>
          <strong>82 GB / 100 GB</strong>
        </div>
        <cv-meter
          class="meter-demo-main-meter"
          value="82"
          min="0"
          max="100"
          low="55"
          high="80"
          optimum="42"
          value-text="82 percent used, high pressure"
          aria-label="Remote vault capacity used"
        >
          high
        </cv-meter>
        <div class="meter-demo-scale" aria-hidden="true">
          <span>low</span>
          <span>optimum</span>
          <span>high</span>
        </div>
      </div>

      <div class="meter-demo-zone-grid" aria-label="Meter threshold examples">
        <div>
          <span>Low</span>
          <cv-meter value="34" low="45" high="80" optimum="62" aria-label="Low threshold example"
            >34</cv-meter
          >
          <strong>Below target</strong>
        </div>
        <div>
          <span>Optimum</span>
          <cv-meter value="58" low="35" high="85" optimum="55" aria-label="Optimum threshold example"
            >58</cv-meter
          >
          <strong>Healthy range</strong>
        </div>
        <div>
          <span>High</span>
          <cv-meter value="91" low="30" high="80" optimum="50" aria-label="High threshold example"
            >91</cv-meter
          >
          <strong>Pressure zone</strong>
        </div>
      </div>
    </div>

    <aside class="meter-demo-compare" aria-labelledby="meter-demo-compare-title">
      <div class="meter-demo-section-header">
        <span class="meter-demo-kicker">Meter vs progress</span>
        <h4 id="meter-demo-compare-title">Choose by meaning, not by shape.</h4>
      </div>

      <div class="meter-demo-compare-grid">
        <div class="meter-demo-compare-card meter-demo-compare-card--meter">
          <span>cv-meter</span>
          <strong>Static measurement</strong>
          <cv-meter
            value="64"
            low="30"
            high="80"
            optimum="60"
            value-text="64 percent strength"
            aria-label="Password strength meter"
          >
            64
          </cv-meter>
          <p>Answers "how much is the measured condition?" and can map the value into threshold zones.</p>
        </div>

        <div class="meter-demo-compare-card meter-demo-compare-card--progress">
          <span>cv-progress</span>
          <strong>Operation progress</strong>
          <cv-progress
            value="64"
            tone="upload"
            value-text="64 percent uploaded"
            aria-label="Archive upload progress"
          >
            64%
          </cv-progress>
          <cv-progress indeterminate aria-label="Manifest verification is still waiting"></cv-progress>
          <p>
            Answers "how far has the job advanced?" and can be indeterminate while work has no known value.
          </p>
        </div>
      </div>
    </aside>
  </section>

  <section class="meter-demo-rules" aria-labelledby="meter-demo-rules-title">
    <div class="meter-demo-section-header">
      <span class="meter-demo-kicker">Decision rules</span>
      <h4 id="meter-demo-rules-title">The visual shape is similar, but the accessibility contract is not.</h4>
    </div>

    <dl class="meter-demo-rule-grid" aria-label="When to use meter or progress">
      <div>
        <dt>Use meter for</dt>
        <dd>disk usage, password strength, queue saturation, memory pressure</dd>
      </div>
      <div>
        <dt>Use progress for</dt>
        <dd>upload, export, sync, verification, loading or completion state</dd>
      </div>
      <div>
        <dt>Meter contract</dt>
        <dd>
          <code>value</code> plus optional <code>low</code>, <code>high</code>, and <code>optimum</code>
        </dd>
      </div>
      <div>
        <dt>Progress contract</dt>
        <dd>
          <code>value</code> while determinate, or <code>indeterminate</code> when the amount is unknown
        </dd>
      </div>
    </dl>
  </section>
</div>

Anatomy

<cv-meter> (host)
└── <div part="base" role="meter">
    └── <div part="indicator" data-status="…">
        └── <span part="label">
            └── <slot>

Attributes

AttributeTypeDefaultDescription
valueNumber0Current measured value
minNumber0Minimum of the range
maxNumber100Maximum of the range (percentage convention)
lowNumberLow threshold boundary
highNumberHigh threshold boundary
optimumNumberOptimum value within the range
value-textString""Custom aria-valuetext string
aria-labelStringAccessible label
aria-labelledbyStringID of labelling element
aria-describedbyStringID of describing element

Slots

SlotDescription
(default)Custom label content rendered inside the indicator

Use value-text for meaningful spoken text; the default slot is visual label content only.

CSS Parts

PartElementDescription
base<div>Root meter element with role="meter"
indicator<div>Fill bar reflecting current percentage and status zone
label<span>Wrapper around the default slot inside the indicator

CSS Custom Properties

PropertyDefaultDescription
--cv-meter-height10pxBlock size of the meter track
--cv-meter-labeled-height18pxDefault track height when label content is present and --cv-meter-height is unset
--cv-meter-border-radius999pxBorder radius of the track and indicator
--cv-meter-label-colorvar(--cv-color-text, #e8ecf6)Text color for the label slot content
--cv-meter-label-font-sizeCalculated from track heightFont size for the label slot content
--cv-meter-transition-durationvar(--cv-duration-normal, 220ms)Transition duration for indicator width
--cv-meter-optimum-colorvar(--cv-color-success, #6ef7c8)Indicator color when status is optimum
--cv-meter-suboptimum-colorvar(--cv-color-warning, #ffbe65)Indicator color when status is low (sub-optimum)
--cv-meter-danger-colorvar(--cv-color-danger, #ff7a8a)Indicator color when status is high (danger zone)
  • The visual label is hidden when the effective track height is below 14px; meter value remains exposed through ARIA.

Additionally, component styles depend on theme tokens through fallback values:

Theme PropertyDefaultDescription
--cv-color-border#2a3245Track border color
--cv-color-surface#141923Track background color
--cv-color-primary#65d7ffDefault indicator color (normal status)
--cv-color-success#6ef7c8Optimum zone color fallback
--cv-color-warning#ffbe65Sub-optimum (low) zone color fallback
--cv-color-danger#ff7a8aDanger (high) zone color fallback
--cv-duration-normal220msTransition duration fallback
--cv-easing-standardeaseTransition timing function

Visual States

Host selectorDescription
[data-status="normal"]Default indicator color using --cv-color-primary
[data-status="optimum"]Indicator uses --cv-meter-optimum-color
[data-status="low"]Indicator uses --cv-meter-suboptimum-color
[data-status="high"]Indicator uses --cv-meter-danger-color

Note: data-status is set on the [part="indicator"] element, not on the host. The status value is derived entirely from the headless model's state.status() computed signal.

Reactive State Mapping

cv-meter is a visual adapter over headless createMeter.

UIKit PropertyDirectionHeadless Binding
valueattr → actionactions.setValue(value)
minattr → optionpassed to createMeter(options)
maxattr → optionpassed to createMeter(options)
lowattr → optionpassed to createMeter(options)
highattr → optionpassed to createMeter(options)
optimumattr → optionpassed to createMeter(options)
value-textattr → optionpassed as formatValueText callback to createMeter(options)
aria-labelattr → optionpassed as ariaLabel to createMeter(options)
aria-labelledbyattr → optionpassed as ariaLabelledBy to createMeter(options)
aria-describedbyattr → optionpassed as ariaDescribedBy to createMeter(options)
Headless StateDirectionDOM Reflection
state.percentage()state → style--cv-meter-width inline style on [part="indicator"]
state.status()state → attrdata-status attribute on [part="indicator"]
  • contracts.getMeterProps() is spread onto the [part="base"] element to apply role, aria-valuenow, aria-valuemin, aria-valuemax, aria-valuetext, aria-label, aria-labelledby, and aria-describedby.
  • When min, max, low, high, optimum, value-text, or ARIA attributes change, the headless model is recreated with new options.
  • When only value changes, actions.setValue(value) is called without recreating the model.
  • UIKit does not compute percentage, status, or ARIA attributes itself. All derived state comes from the headless model.

Events

None. Meter is an output-only (read-only) component with no user interaction.

ARIA

All accessibility semantics are provided by the headless contract getMeterProps():

  • role="meter" on [part="base"]
  • aria-valuenow reflecting current value
  • aria-valuemin reflecting minimum
  • aria-valuemax reflecting maximum
  • aria-valuetext when value-text attribute is set (via formatValueText callback)
  • aria-label, aria-labelledby, aria-describedby pass-through when provided

The UIKit layer does not construct any ARIA attributes directly.

ChromVoid UIKit documentation