Gauge
Instrument, wave 2 (plan). Follows the anatomy template and the conventions.
1. Purpose and native element
Section titled “1. Purpose and native element”Shows a bounded measurement on a round dial, as on instrument panels: a temperature, a pressure, a speed. Renders a
native <meter> (implicit role meter) with min, max, low, high and optimum, following the WAI-ARIA
Meter pattern; Strata adds aria-valuetext for units. A Gauge is read-only:
it takes no pointer and no keys.
Why <meter> and not <progress>: the plan allows either. A gauge shows where a measurement stands within a range, with
zones, which is what a meter is; <progress> is a task’s completion and has an indeterminate state the dial cannot show.
Indeterminate is therefore not supported: a wait is a Spinner, a task’s progress a
ProgressBar. The Gauge is the Meter’s round sibling: the same native element, attributes,
behaviour and markup parts (ug-meter__*), on a round dial.
2. Object
Section titled “2. Object”A round dial under glass: a polished bezel ring round a window set with the printed scale and the needle on its hub, over the native meter. Labels are printed on the face, never on the object.
| Part | Shape | Height at 50% | Moves |
|---|---|---|---|
| Box (the native meter) | Square, size.gauge + 24 (184 at medium) |
0 | No |
| Bezel | A ring 6 px in from the box, outer Ø size.gauge + 12, 6 wide to the window |
Relief and Objects: fitted to the box (2.06 at most); Instrument: 3 | No |
| Window | A round well Ø size.gauge (120, 160 or 200), set in the bezel |
Relief: elevation.well; Objects: a well object |
No |
| Scale | Ticks (6 × 1.2 px) at the radius size.gauge / 2 − 8 and numerals inside them, over gauge.sweep (240°), the middle at the top |
0 | No |
| Zone band | A band 3.5 px wide outside the ticks, in colour.danger, below low and above high |
0 | No |
| Needle | A blade 2.2 wide from a 12 px tail to the ticks (R − 4) | Captured at Relief and Objects; a part 2.4 high on the glass at Instrument | Turns through gauge.sweep on spring.gauge |
| Hub | Ø 12 at the centre | Relief: a dome | No |
-
Zones. A Gauge prints a red zone outside the ticks wherever the value is beyond
loworhigh, when the author gives them:highis where the redline starts,lowwhere a low zone ends. (The Meter’s three colours come from the native rule withoptimum; a dial has one warning colour, the one a redline has.)optimumstays on the native meter for assistive technology. -
At the Instrument level
gpu/instrument-meter.tsbuilds the object, bounded by the native meter’s box:Part Profile (50% depth; heights × 0.6, 1, 1.6 with depth) Material (premium) Pose bezelLathe ring from the window’s radius out to R + 6, 3 high, a 0.6 edge break knobSkirt: polished (250, 250, 251), roughness 0.04None windowA prism over the dial (a circle), 2.4 high, a 2.2 px bevel, clear: its flat region is the capture, so the scale, the zones and the legend under it are exact meterGlass: glass (index 1.5, absorption 0.03, reflection 1.5)None needleExtruded blade, 2.2 wide, from 12 px behind the pivot to R − 4, standing 2.4 over the glass knobInkTurn −120° − 90°to120° − 90°about the pivotpivotLathe hub Ø 12, 4 high knobCapNone tis the value’s place along the range, driven byspring.gauge(160 / 20†: heavier and slower than a meter’s, a settle of about 330 ms) in fixed 4 ms steps, clamped at the stops, instant under reduced motion. The needle is a pose, so a live value is a uniform write with no capture. The needle and the hub write depth and stand over the glass, so the window’s exact pixels never hide them. The caster is none (a turning blade’s footprint is a large box). Every vertex stays at least 0.5 px inside the box over the whole travel in every corner of the reference stage at every depth (checkContainment). -
At Relief and Objects the bezel is a raised face or slab (fitted so its shadow stays in the box), the window a well, the hub a dome; the needle and the scale are captured content turning with
--strata-meter-tonspring.gaugeas a CSS transition (328 ms), which the helper follows frame by frame.
3. Faces and materials
Section titled “3. Faces and materials”| Face | Colour role | Text role | Material role |
|---|---|---|---|
| Bezel | knob metal |
— | knob (satin); polished at Instrument |
| Window | panel, a 1 px edge border |
— | field (a well) |
| Needle | edge mixed 55% towards black (text in the high-contrast themes) |
— | None (captured) |
| Hub | knob |
— | knob |
| Ticks | text |
— | None (printed) |
| Numerals | — | muted |
None (printed) |
| Red zone | danger |
— | None (printed) |
| Legend | — | text (small, strong, uppercase) |
None (printed) |
4. Elevation per state
Section titled “4. Elevation per state”| State | Relief | Objects | Instrument | CSS |
|---|---|---|---|---|
| Rest | A raised bezel, a well, a hub dome | A slab, a well object, a dome | A glass window, the polished bezel, the needle | The relief recipe on the bezel; the window’s inner shadow |
| Value change | The needle turns on its spring (captured) | As Relief | The needle’s pose on spring.gauge: no capture |
--strata-meter-t follows --strata-meter-value on a linear() |
Hover, pressed, focus-visible, disabled, selected, invalid and busy do not apply. Under prefers-reduced-motion the needle moves
at once.
5. Relief primitives
Section titled “5. Relief primitives”A raised bezel (a slab at Objects), a well window (a well object at Objects) and a knob hub. The needle has no face. The
<meter> carries no data-strata-face.
6. Tokens it reads
Section titled “6. Tokens it reads”| Token | Prop |
|---|---|
size.gauge (120, 160, 200, --strata-gauge-size): the dial’s diameter |
size |
gauge.sweep (240°, 90–330) |
— |
spring.gauge (stiffness 160, damping 20†) |
— |
colour.danger, colour.edge, colour.text, colour.muted, colour.knob |
— |
elevation.raised, elevation.well, state.*, interior.max, bevel.*, shadow.* |
— |
material.roles.knob, material.roles.field |
material |
font.size.caption, font.size.small, font.weight.strong, space.1 |
— |
level |
level |
† Provisional, with the premium character. Props: label, value, min (0), max (100), low, high, optimum,
marks (a count, or values with labels; 5 by default), unit or format (aria-valuetext), id, size, material,
level, and in React meterRef (Vue exposes root and meter).
7. DOM structure and overlay needs
Section titled “7. DOM structure and overlay needs”<div class="ug-meter ug-meter--dial ug-gauge" style="--strata-meter-value: 0.6; --strata-meter-lo: 0.1667; --strata-meter-hi: 0.8333;"> <div class="ug-meter__body"> <span class="ug-meter__object" aria-hidden="true"> <span class="ug-meter__bezel" data-strata-face="raised"> <span class="ug-meter__window" data-strata-face="well"> <span class="ug-meter__zones"></span> <span class="ug-meter__scale"> <span class="ug-meter__mark" data-strata-value="0" style="--strata-meter-at: 0;">0</span> <span class="ug-meter__mark" data-strata-value="30" style="--strata-meter-at: 0.25;">30</span> <span class="ug-meter__mark" data-strata-value="60" style="--strata-meter-at: 0.5;">60</span> <span class="ug-meter__mark" data-strata-value="90" style="--strata-meter-at: 0.75;">90</span> <span class="ug-meter__mark" data-strata-value="120" style="--strata-meter-at: 1;">120</span> </span> <span class="ug-meter__needle"></span> <span class="ug-meter__hub" data-strata-face="knob"></span> </span> </span> </span> <meter class="ug-meter__native" id="temperature" min="0" max="120" value="72" low="20" high="100" data-strata-unit=" °C" aria-valuetext="72 °C">72 °C</meter> </div> <label class="ug-meter__label" for="temperature">Temperature</label></div>- The
.ug-meterroot (withug-meter--dialandug-gauge) is the boundary: refs,classandstylereach it.--strata-meter-value(0–1) is the value’s place,--strata-meter-loand--strata-meter-hithe red zones’ edges (set whenloworhighis given). The native<meter>is transparent and covers the whole object;attachStrataMeters(andattachStrataGauges, its Gauge entry point) from@underglass/ui/domkeeps the drawing andaria-valuetextin step with it.
8. Keyboard and screen reader
Section titled “8. Keyboard and screen reader”A Gauge has no keyboard interaction: it is not focusable and takes no input. Role meter (native), named by the legend,
with the value, minimum, maximum and aria-valuetext when a unit or a format gives one (the manual screen-reader script is
VAL-002’s). Meaning never rides on colour alone: the needle’s place against the printed scale gives the value, and the red
zone’s extent is the scale’s. Under forced colours and more contrast the needle, the scale and the zone band take the text
colour (the band as a CanvasText strip). A Gauge’s live value goes through driveStrataMeter(meter, value)
(@underglass/ui/dom): at the Instrument level it writes nothing to the DOM, the value reaching the GPU as the needle’s pose
(300 frames: 0 captures, 0 uploads, 0 hit-geometry updates), and the native value follows at a low rate for assistive technology;
at Relief and Objects the needle is captured content and the drawing’s value is written each time.
9. CSS presentation
Section titled “9. CSS presentation”The relief recipe on the bezel (a metal gradient in the knob’s colour, lit from the light), a round window with an inner
shadow, the printed scale (ticks and numerals at calc() angles on whole pixels), the zone band (a conic gradient masked to a
ring) and the needle turning with --strata-meter-t. The geometry is in container units (cqw), so a gauge scales with its
width and never exceeds its container: a 320 px viewport reflows. The window’s padding and the numerals’ radius grow with the
caption size, so 200% text keeps the numerals inside the dial. Forced colours, more contrast and reduced motion are the Meter’s
(meter.md).
10. Tests
Section titled “10. Tests”- Plain DOM (
ui-gauge.test.mjs, real markup): rolemeter, name, value, range, zones andaria-valuetext; a writtenvalueturns the needle (--strata-meter-value) and the unit’saria-valuetext; every box and every size is as documented, and a grid ofelementFromPointover it finds the meter; 320 and 390 px; 200% text; axe in light and dark, face mode, forced colours and more contrast; reduced motion moves the needle at once. - GPU (
ui-gauge.test.mjs, DPR 1 and 2, Relief, Objects and Instrument): GPU stays on; at Instrument the scene is version 4 with a bezel, a window, a needle and a hub per gauge and the needle onspring.gauge; at 0, 25, 50, 75 and 100 of the range and in flight the parts draw inside the boxes only (0 pixels outside) and native hits on every part pixel find the meter; a value change changes pixels in its box only; the printed scale under the glass is the capture itself (the faceplate pixels under the glass are the capture’s colour to a level, and a needle at another value leaves the numerals it does not cross pixel for pixel the same); 300 frames of a live value add no capture, upload or hit-geometry update while the needle moves on its spring; reduced motion puts the needle on its target in the first frame; the accessibility snapshot is the same in DOM and GPU modes. Checked in a headful Canary window as well. - Bindings and Astro: with the Meter’s,
ui-meter-bindings.test.mjsandui-meter-astro.test.mjs. - Unit:
strata-meter.unit.test.mjs, with the Meter’s: the dial’s parts validated by core’svalidateInstrumentSceneand contained over the whole travel (checkContainment). - Not yet: a source prop (live audio), a low-end GPU, and the manual screen-reader script.