Skip to content

timer band

A band beneath the topbar that shows elapsed time against a goal (e.g. “2-minute swap”), with warn and over-goal states. Persistent across screens within a flow — its job is to give the operator continuous awareness that the clock is running.

The progress indicator is a circular ring, not a linear bar. Linear progress would visually compete with the [[step-stepper|step-dots stepper]] in the same vertical region; the ring is unmistakably the timer.

  • A flow has a service-level target (battery swap goal: 2 minutes; QA inspection goal: 5 minutes)
  • The operator’s job is to hit the target consistently
  • Awareness of remaining time should be passive — visible but not intrusive

Not for: countdown timers users explicitly wait for (use a [[status-card]] with elapsed text), one-off processes (use inline copy).

╭───╮ Swap time
( 1:23 ) of 2:00 goal
╰───╯
  • Container .timer-band: padding: 4px 20px 12px, display: flex, align-items: center, gap: 12px, flex-shrink: 0.
  • Ring .timer-ring: 44×44px wrapper. SVG viewBox="0 0 40 40", two centered <circle>s at (20,20), r=18, stroke-width 4. Track stroke var(--grey4). Fill stroke var(--g-primary), stroke-linecap: round, stroke-dasharray: 113.097 (= 2π·18), stroke-dashoffset interpolated from full circumference (empty) to 0 (full ring). SVG rotated -90deg so the arc starts at 12 o’clock and grows clockwise.
  • Ring value: absolutely positioned text inside the ring, 11px / 700 tabular-nums, letter-spacing: -0.02em so 1:23 fits comfortably. Reads elapsed (1:23) while under, over-goal form (+0:43) once exceeded.
  • Meta: stacked label + goal beside the ring. Label Swap time at 12px / 600. Goal of 2:00 goal at 11px / 500 grey, flips to over goal once exceeded.
State Condition Ring fill Value + goal text
good elapsed < (goal − 30s) --g-primary --g-dark
warn (goal − 30s) ≤ elapsed < goal --amber10 --amber11
over elapsed ≥ goal --red9 --red9

Apply the state class to the .timer-band root; ring fill, value, and goal text recolor via descendant selectors.

  • Single setInterval(tick, 1000) for the active swap. Don’t spin up per-screen intervals.
  • Hide on dashboard, support log, and success/complete screens — anywhere the swap isn’t actively in motion.
  • Inject via a DOMContentLoaded hook that finds every .topbar and prepends a .timer-band (skip topbars on excluded screens). This keeps the markup DRY across many screens.
  • Display in elapsed form (1:23) while under goal, then flip to over-goal form (+0:43) once exceeded.
  • Audio or haptic on cross-threshold — the visual color shift is sufficient, alarms would be invasive in a high-touch retail context.
  • Resetting on screen change — the timer follows the swap, not the screen.
  • Pause control — operators don’t pause the timer; if they need to stash work, they [[parked-queue|park the swap]] entirely.

See .timer-band / .timer-ring* CSS, tickTimers(), and updateTimerVisibility() in products/rechaj-swap-app/prototypes/support-cases-v3.html.