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.