···11-import { useEffect, useLayoutEffect } from 'react';
22-33-// useLayoutEffect causes warnings during SSR; fall back to useEffect on the server.
44-const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
55-66-/**
77- * Synchronises the CSS animation identified by `animationName` across every element currently playing it, so they
88- * all pulse in lockstep rather than each starting from their own offset.
99- *
1010- * Pass the animation name string (e.g. `skeletonAnimationName`), or a falsy value to skip syncing (e.g. when the
1111- * animated element isn't rendered).
1212- */
1313-export function useSynchronizeAnimations(animationName: string | null | undefined): void {
1414- useIsomorphicLayoutEffect(() => {
1515- if (animationName) scheduleSync(animationName);
1616- }, [animationName]);
1717-}
1818-1919-// Animation names awaiting a sync, drained by a single rAF so any number of
2020-// elements mounting in the same frame costs one pass over document.getAnimations().
2121-const pendingNames = new Set<string>();
2222-let frameId: number | null = null;
2323-2424-function scheduleSync(animationName: string): void {
2525- // Pre-2020 browsers lack these Web Animations APIs; animations still run, just not in lockstep.
2626- if (typeof CSSAnimation === 'undefined' || typeof document.getAnimations !== 'function') {
2727- return;
2828- }
2929-3030- pendingNames.add(animationName);
3131- if (frameId !== null) return;
3232-3333- frameId = requestAnimationFrame(() => {
3434- frameId = null;
3535- // Align every animation to the clock of the first one seen with its name.
3636- const referenceTimes = new Map<string, number>();
3737- for (const animation of document.getAnimations()) {
3838- if (!(animation instanceof CSSAnimation) || !pendingNames.has(animation.animationName)) {
3939- continue;
4040- }
4141- const referenceTime = referenceTimes.get(animation.animationName);
4242- if (referenceTime !== undefined) {
4343- animation.currentTime = referenceTime;
4444- } else if (typeof animation.currentTime === 'number') {
4545- referenceTimes.set(animation.animationName, animation.currentTime);
4646- }
4747- }
4848- pendingNames.clear();
4949- });
5050-}
···55<LoadingSpinner aria-label="Loading" />
66```
7788+All mounted indeterminate spinners rotate and pulse in sync, even when they mount at different
99+times.
1010+811## Progress mode
9121013Omit `value` for indeterminate progress. Pass `value` for determinate.
···1313 skeletonBorderRadiusVar,
1414} from '../recipes/loading-skeleton.css.js';
1515export type { LoadingSpinnerVariants } from '../recipes/loading-spinner.css.js';
1616-export { spinner as loadingSpinner } from '../recipes/loading-spinner.css.js';
1616+export {
1717+ rubberBandAnimationName,
1818+ spinAnimationName,
1919+ spinner as loadingSpinner,
2020+} from '../recipes/loading-spinner.css.js';
1721export type {
1822 TextAlign,
1923 TextColor,
···11+import { useEffect, useLayoutEffect } from 'react';
22+33+// useLayoutEffect causes warnings during SSR; fall back to useEffect on the server.
44+const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
55+66+/**
77+ * Synchronises the CSS animation identified by `animationName` across every element currently playing it, so they
88+ * all pulse in lockstep rather than each starting from their own offset.
99+ *
1010+ * Pass the animation name string (e.g. `skeletonAnimationName`), or a falsy value to skip syncing (e.g. when the
1111+ * animated element isn't rendered).
1212+ */
1313+export function useSynchronizeAnimations(animationName: string | null | undefined): void {
1414+ useIsomorphicLayoutEffect(() => {
1515+ if (animationName) scheduleSync(animationName);
1616+ }, [animationName]);
1717+}
1818+1919+// Animation names awaiting a sync, drained by a single rAF so any number of
2020+// elements mounting in the same frame costs one pass over document.getAnimations().
2121+const pendingNames = new Set<string>();
2222+let frameId: number | null = null;
2323+2424+function scheduleSync(animationName: string): void {
2525+ // Pre-2020 browsers lack these Web Animations APIs; animations still run, just not in lockstep.
2626+ if (typeof CSSAnimation === 'undefined' || typeof document.getAnimations !== 'function') {
2727+ return;
2828+ }
2929+3030+ pendingNames.add(animationName);
3131+ if (frameId !== null) return;
3232+3333+ frameId = requestAnimationFrame(() => {
3434+ frameId = null;
3535+ // Align every animation to the clock of the first one seen with its name.
3636+ const referenceTimes = new Map<string, number>();
3737+ for (const animation of document.getAnimations()) {
3838+ if (!(animation instanceof CSSAnimation) || !pendingNames.has(animation.animationName)) {
3939+ continue;
4040+ }
4141+ const referenceTime = referenceTimes.get(animation.animationName);
4242+ if (referenceTime !== undefined) {
4343+ animation.currentTime = referenceTime;
4444+ } else if (typeof animation.currentTime === 'number') {
4545+ referenceTimes.set(animation.animationName, animation.currentTime);
4646+ }
4747+ }
4848+ pendingNames.clear();
4949+ });
5050+}