Timer
An elapsed-time readout paired with a hairline-separated cluster of icon-only actions.
Usage
import { Timer, ButtonGroupItem } from '@acronis-platform/ui-react';Timer is a bordered 32px box holding an elapsed-time readout next to a cluster
of icon-only actions. The readout is a role="timer" live region rendered with
tabular figures, so the box keeps its width as the digits change. Themed by the
--ui-timer-* tokens.
It holds no clock. Timer renders whatever value you hand it — the
interval, the format, and the state the actions mutate are all yours. That's
what lets the same component serve a stopwatch, a countdown, and a static
duration.
The actions are ButtonGroupItem children. Timer renders the ButtonGroup
itself, always in its inlined style, because its own box already draws the
border and radius an outlined group would duplicate — so the separators, the
single Tab stop, and the arrow-key roving all come from ButtonGroup unchanged.
Each action is icon-only and needs its own aria-label; the toolbar as a whole
is named by actionsLabel, whose default ("Timer actions") is untranslated
English.
Examples
The design's composition — pause, rename, add an entry:
import {
CirclePauseIcon,
PencilIcon,
PlusIcon,
} from '@acronis-platform/icons-react/stroke-mono';
<Timer value={formatDuration(elapsed)} actionsLabel="Time tracking">
<ButtonGroupItem aria-label="Pause" onClick={pause}>
<CirclePauseIcon size={16} />
</ButtonGroupItem>
<ButtonGroupItem aria-label="Rename" onClick={rename}>
<PencilIcon size={16} />
</ButtonGroupItem>
<ButtonGroupItem aria-label="Add entry" onClick={addEntry}>
<PlusIcon size={16} />
</ButtonGroupItem>
</Timer>;Read-only — with no actions the toolbar is not rendered at all, and the divider goes with it:
<Timer value="00:12:30" />A stopwatch. The interval belongs to the caller, and a play/pause control's accessible name must track its current function rather than its icon:
const [seconds, setSeconds] = useState(0);
const [running, setRunning] = useState(false);
useEffect(() => {
if (!running) return;
const id = setInterval(() => setSeconds((s) => s + 1), 1000);
return () => clearInterval(id);
}, [running]);
<Timer value={format(seconds)} actionsLabel="Stopwatch">
<ButtonGroupItem
aria-label={running ? 'Pause' : 'Resume'}
onClick={() => setRunning((r) => !r)}
>
{running ? <CirclePauseIcon size={16} /> : <CirclePlayIcon size={16} />}
</ButtonGroupItem>
<ButtonGroupItem
aria-label="Stop"
onClick={() => {
setRunning(false);
setSeconds(0);
}}
>
<CircleStopIcon size={16} />
</ButtonGroupItem>
</Timer>;An action can be disabled like any other ButtonGroupItem — it stays focusable
and is marked aria-disabled, per the WAI-ARIA toolbar pattern:
<Timer value="01:20:00">
<ButtonGroupItem aria-label="Pause">
<CirclePauseIcon size={16} />
</ButtonGroupItem>
<ButtonGroupItem aria-label="Discard" disabled>
<BinIcon size={16} />
</ButtonGroupItem>
</Timer>Accessibility
role="timer" is implicitly aria-live="off", which is deliberate: announcing
a ticking clock once a second would drown out everything else on the page. Don't
raise it to polite. If a milestone matters — a limit reached, a session about
to expire — announce that event from a separate status region.
API Reference
Prop
Type
The actions accept the full ButtonGroupItem API — see
ButtonGroup.