Components / SignalField

SignalField

A backdrop for a surface that would otherwise be a flat empty plane and that has a run to report on. Dots scattered across the container are pulled home into the Layers mark as progress advances. The onboarding canvas is the consumer.

import { SignalField } from '@layers/ui/signal-field'

Rendered stock

House rules

  • progress is required and takes no default. A field that showed a finished mark because a caller forgot to wire one up would be claiming a run had finished.
  • Feed it a reading derived from facts, never a timer, and latch it to its own maximum. A mark that comes apart is reporting a run going backwards.
  • The field positions itself absolute inset-0. Give it a relative parent and render it BEFORE the content, which then sits above it on DOM order alone. It is never inside a scroll region: an absolute child there is positioned in the scrolled coordinate space and slides away with the content.
  • It is aria-hidden and pointer-events-none, and it has no pointer behaviour at all. It cannot take a click meant for a card.
  • intensity="ambient" is the default and the only one safe under reading content: text-muted-foreground sitting directly on a dot still clears 4.6:1 in both themes.
  • intensity="display" is roughly double, for a surface whose only content is opaque cards and chips. Muted body text over it falls under AA, so it is not the value to reach for when in doubt.
  • The mark is sampled from the real logo path, so it cannot drift from the mark the rest of the app renders. Both inks come from text-foreground and text-muted-foreground on the field own tree, so it follows the theme, including a subtree that inverts its ink.
  • The loop parks while the field is off screen and once every dot is home. Reduced motion keeps the assembly (that is information) and drops the idle drift (that is decoration), so the loop never starts at all.

See also