WorkingIndicator
Inline indicator for long-running work: a pixel grid, a shimmering label, and a live elapsed clock.
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
TxTypingIndicatorwhen someone is composing, andTxSpinnerfor 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
showElapsedoff for instant operations and save both the interval and the visual noise. - Always pass
startedAtin 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
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | 'Working' | The shimmering status text. |
variant | 'drive' | 'dots' | 'orbit' | 'drive' | Pixel-grid pattern. |
startedAt | number | — | Clock origin in epoch milliseconds. Omit to count from mount. |
showElapsed | boolean | true | Turning it off drops the readout and stops the interval. |
elapsedFormatter | (ms: number) => string | — | Overrides the default format (12.3s / 2m 3.0s). |
ariaLabel | string | — | Accessible name for the status region. Omit to let the visible label be the announcement. |
Slots
| Name | Scope | Description |
|---|---|---|
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 carriesaria-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
ariaLabelis omitted noaria-labelis 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 isopacity: .07and 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
useElapsedandformatElapsedfor 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.tsexportsuseElapsedandformatElapsed. - 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,startedAtre-basing,showElapsedstopping 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 namedTxLoadingStatein 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.