Components / Signal Meter
Signal Meter
A quantity already held, read as a gauge — how much of a project’s available signal Layers actually has. Never for work in flight.
import { SignalMeter } from '@layers/ui/signal-meter'Rendered stock
House rules
- Pass `interactive` when the whole card is a link, which adds the hover fill the other cards on a bento carry. It cannot come from `className`: that lands on the outer section, and a hover fill on a wrapper that is not the painted surface does nothing visible. The caller still owns the anchor — this is only the affordance.
- A HARD FORK of the marketing instrument (founder ruling 2026-08-05). apps/marketing has its own SignalMeter and will never consume this one; the styles are allowed to diverge and neither side is a bug report about the other. Do not factor them together.
- Not Progress, and the difference is semantic. Progress is a track with one width-sized indicator — a continuous fill, which reads as loading. This is a quantity already held, so it is discrete ticks. If you are showing a job in progress, you want Progress.
- It draws the instrument and owns none of the arithmetic. The reading comes from signalStrength() in @layers/shared-types — two surfaces scoring the same project differently is the bug that separation prevents.
- What to DO about the reading is `children`, not props. A shortlist of next moves or an SDK offer is product logic and copy; a design system has no business holding a GitHub access state. Passing children draws the card divider; a meter with nothing under it rules a line across nothing.
- A reading above zero never renders an empty strip: the number and the gauge must not disagree on one line.
- Pass `unknown` when ANY input behind `value` failed to load. The arithmetic still returns a number in that case, and that number is whatever the missing input floors to — so a customer with four live sources reads 15% in 28px, which is the most trusted figure on the page being confidently wrong. `unknown` draws a dark strip and the null glyph instead. Not a Skeleton: a skeleton says “still coming”, and a read that failed is not still coming.
- The cascade is transition-delay keyed to segment INDEX, disabled wholesale under prefers-reduced-motion — both the delay and the colour transition, since 40 staggered segments is 640ms of movement.
- The unlit track is dimmer in light mode than dark, and that asymmetry is measured rather than a preference — see the contrast note in the component.