Components/WorkingIndicator

WorkingIndicator

Inline indicator for long-running work: a pixel grid, a shimmering label, and a live elapsed clock.

VerifiedSince 0.3.9

Usage

WorkingIndicator

Loading demo...

Pixel Patterns

drive and dots share one chevron wavefront on a 650ms cycle; orbit is a comet lapping the grid perimeter, and its centre cell never lights.

Pixel patterns

How drive, dots, and orbit differ in wavefront and cell shape.

Loading demo...

Best Practices

  • Use it for "something is running, and here is how long it has been running". Reach for TxTypingIndicator when someone is composing, and TxSpinner for a wait with no semantics.
  • Name the work in the label ("Indexing the repository") rather than writing "Working" — the readout already answers how long, so the label should answer what.
  • A readout is only worth showing past a few seconds; turn showElapsed off for instant operations and save both the interval and the visual noise.
  • Always pass startedAt in streaming surfaces, or the clock snaps back to zero whenever the component is rebuilt.
  • The component sizes to fit-content; the container is the host's decision.

API Reference

Props

PropTypeDefaultDescription
labelstring'Working'The shimmering status text.
variant'drive' | 'dots' | 'orbit''drive'Pixel-grid pattern.
startedAtnumber—Clock origin in epoch milliseconds. Omit to count from mount.
showElapsedbooleantrueTurning it off drops the readout and stops the interval.
elapsedFormatter(ms: number) => string—Overrides the default format (12.3s / 2m 3.0s).
ariaLabelstring—Accessible name for the status region. Omit to let the visible label be the announcement.

Slots

NameScopeDescription
label—Replaces the shimmering label, for rich content.

Timing and Remounts

Without startedAt the clock counts from mount. A streaming host may rebuild this row on every delta, which resets it — passing a timestamp lets the reading survive a remount.

The reading is always Date.now() - startedAt rather than a self-incrementing counter: a backgrounded tab throttles timers, and a counter that adds a fixed step per fire falls further behind wall clock the longer it stays hidden.

Overview

  • The component has no emits: it presents state and raises nothing.
  • The root is role="status". The readout carries aria-hidden="true" — it changes ten times a second, and inside a live region every tick would be announced. The label alone is what gets spoken.
  • When ariaLabel is omitted no aria-label is written. Duplicating the visible text there gets it read twice.
  • The nine cells' animation delays live in SCSS :nth-child() rules, not inline styles. Inline styles outrank the reduced-motion rule and would quietly defeat it.
  • In orbit, the centre cell is opacity: .07 and never animates; every other cell rests at .15.
  • Under reduced motion the grid freezes in its dim state (animation: none) and the clock keeps ticking — it reports real progress, so it is not decoration.
  • The directory also exports useElapsed and formatElapsed for hosts that want to lay the reading out themselves.

Technologies

  • Component source: packages/tuffex/packages/components/src/working-indicator/src/TxWorkingIndicator.vue.
  • Composable: packages/tuffex/packages/components/src/working-indicator/src/use-elapsed.ts exports useElapsed and formatElapsed.
  • Types: packages/tuffex/packages/components/src/working-indicator/src/types.ts.
  • Verified coverage: packages/tuffex/packages/components/src/working-indicator/__tests__/working-indicator.test.ts (17 cases) covers formatting boundaries, startedAt re-basing, showElapsed stopping the interval, unmount cleanup, variant classes, and aria ownership.
  • Adapted from Beautiful UI (https://www.beautifului.dev), © 2026 Shane Levine, MIT.
查看源码
packages/tuffex/packages/components/src/working-indicator/index.ts
  • Unrelated to TxLoadingState: the similarly named TxLoadingState in this package is a page-level empty-state wrapper with entirely different semantics. Do not swap one for the other.
  • Accessibility: hiding the readout from assistive tech is a deliberate trade — ten announcements a second is not usable. When elapsed time genuinely matters to a screen-reader user, have the host announce the total once the task finishes.
  • Reduced motion: what stops is the tweening, not the state machine. The clock continuing to run matches upstream behaviour.