use-scroll-animate 4.2.0 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/dist/chunks/{base-BPG5zvex.js → base-BtJDNCB6.js} +45 -5
- package/dist/chunks/base-BtJDNCB6.js.map +1 -0
- package/dist/chunks/{base-CXx7jZ-o.cjs → base-DE2yzxE7.cjs} +47 -4
- package/dist/chunks/base-DE2yzxE7.cjs.map +1 -0
- package/dist/chunks/{core-BRIIoKeS.cjs → core-BC9S2osy.cjs} +2 -2
- package/dist/chunks/{core-BRIIoKeS.cjs.map → core-BC9S2osy.cjs.map} +1 -1
- package/dist/chunks/{core-IaorbTdu.cjs → core-DPYjLqA0.cjs} +2 -2
- package/dist/chunks/{core-IaorbTdu.cjs.map → core-DPYjLqA0.cjs.map} +1 -1
- package/dist/chunks/{core-LkGRmgES.js → core-DS1nrx5L.js} +2 -2
- package/dist/chunks/{core-LkGRmgES.js.map → core-DS1nrx5L.js.map} +1 -1
- package/dist/chunks/{core-eXX8rB_b.js → core-DX6pdOoY.js} +2 -2
- package/dist/chunks/{core-eXX8rB_b.js.map → core-DX6pdOoY.js.map} +1 -1
- package/dist/chunks/{spring-BziPsW3r.js → spring-CxRz49Wk.js} +2 -2
- package/dist/chunks/{spring-BziPsW3r.js.map → spring-CxRz49Wk.js.map} +1 -1
- package/dist/chunks/{spring-2OTnYCzm.cjs → spring-hsmgd7ml.cjs} +2 -2
- package/dist/chunks/{spring-2OTnYCzm.cjs.map → spring-hsmgd7ml.cjs.map} +1 -1
- package/dist/chunks/{variants-BNIrn4ch.cjs → variants-CM2zaG8v.cjs} +2 -2
- package/dist/chunks/{variants-BNIrn4ch.cjs.map → variants-CM2zaG8v.cjs.map} +1 -1
- package/dist/chunks/{variants-BVYMcxRv.js → variants-dD6SbUMR.js} +2 -2
- package/dist/chunks/{variants-BVYMcxRv.js.map → variants-dD6SbUMR.js.map} +1 -1
- package/dist/components/a11y.cjs +239 -0
- package/dist/components/a11y.cjs.map +1 -0
- package/dist/components/a11y.d.cts +120 -0
- package/dist/components/a11y.d.ts +120 -0
- package/dist/components/a11y.js +224 -0
- package/dist/components/a11y.js.map +1 -0
- package/dist/components/angular.cjs +7 -6
- package/dist/components/angular.cjs.map +1 -1
- package/dist/components/angular.d.cts +80 -76
- package/dist/components/angular.d.ts +80 -76
- package/dist/components/angular.js +7 -6
- package/dist/components/angular.js.map +1 -1
- package/dist/components/background.cjs +2 -2
- package/dist/components/background.d.cts +10 -0
- package/dist/components/background.d.ts +10 -0
- package/dist/components/background.js +3 -3
- package/dist/components/cards.cjs +2 -2
- package/dist/components/cards.d.cts +10 -0
- package/dist/components/cards.d.ts +10 -0
- package/dist/components/cards.js +3 -3
- package/dist/components/click.cjs +2 -2
- package/dist/components/click.d.cts +10 -0
- package/dist/components/click.d.ts +10 -0
- package/dist/components/click.js +3 -3
- package/dist/components/depth.cjs +3 -3
- package/dist/components/depth.d.cts +10 -0
- package/dist/components/depth.d.ts +10 -0
- package/dist/components/depth.js +4 -4
- package/dist/components/feedback.cjs +1 -1
- package/dist/components/feedback.d.cts +10 -0
- package/dist/components/feedback.d.ts +10 -0
- package/dist/components/feedback.js +2 -2
- package/dist/components/gesture.cjs +3 -3
- package/dist/components/gesture.d.cts +10 -0
- package/dist/components/gesture.d.ts +10 -0
- package/dist/components/gesture.js +5 -5
- package/dist/components/interaction.cjs +1 -1
- package/dist/components/interaction.d.cts +10 -0
- package/dist/components/interaction.d.ts +10 -0
- package/dist/components/interaction.js +2 -2
- package/dist/components/layout.cjs +1 -1
- package/dist/components/layout.d.cts +10 -0
- package/dist/components/layout.d.ts +10 -0
- package/dist/components/layout.js +2 -2
- package/dist/components/packs.cjs +1 -1
- package/dist/components/packs.d.cts +10 -0
- package/dist/components/packs.d.ts +10 -0
- package/dist/components/packs.js +2 -2
- package/dist/components/page.cjs +6 -3
- package/dist/components/page.cjs.map +1 -1
- package/dist/components/page.d.cts +10 -0
- package/dist/components/page.d.ts +10 -0
- package/dist/components/page.js +7 -4
- package/dist/components/page.js.map +1 -1
- package/dist/components/physics.cjs +2 -2
- package/dist/components/physics.d.cts +10 -0
- package/dist/components/physics.d.ts +10 -0
- package/dist/components/physics.js +4 -4
- package/dist/components/reveal.cjs +1 -1
- package/dist/components/reveal.d.cts +10 -0
- package/dist/components/reveal.d.ts +10 -0
- package/dist/components/reveal.js +2 -2
- package/dist/components/solid.cjs +7 -6
- package/dist/components/solid.cjs.map +1 -1
- package/dist/components/solid.d.cts +80 -76
- package/dist/components/solid.d.ts +80 -76
- package/dist/components/solid.js +7 -6
- package/dist/components/solid.js.map +1 -1
- package/dist/components/svelte.cjs +7 -6
- package/dist/components/svelte.cjs.map +1 -1
- package/dist/components/svelte.d.cts +80 -76
- package/dist/components/svelte.d.ts +80 -76
- package/dist/components/svelte.js +7 -6
- package/dist/components/svelte.js.map +1 -1
- package/dist/components/svg.cjs +1 -1
- package/dist/components/svg.d.cts +10 -0
- package/dist/components/svg.d.ts +10 -0
- package/dist/components/svg.js +2 -2
- package/dist/components/text.cjs +284 -11
- package/dist/components/text.cjs.map +1 -1
- package/dist/components/text.d.cts +166 -3
- package/dist/components/text.d.ts +166 -3
- package/dist/components/text.js +280 -13
- package/dist/components/text.js.map +1 -1
- package/dist/components/timeline.cjs +2 -2
- package/dist/components/timeline.d.cts +10 -0
- package/dist/components/timeline.d.ts +10 -0
- package/dist/components/timeline.js +4 -4
- package/dist/components/transitions.cjs +1 -1
- package/dist/components/transitions.d.cts +10 -0
- package/dist/components/transitions.d.ts +10 -0
- package/dist/components/transitions.js +2 -2
- package/dist/components/ui.cjs +3 -3
- package/dist/components/ui.d.cts +10 -0
- package/dist/components/ui.d.ts +10 -0
- package/dist/components/ui.js +5 -5
- package/dist/components/vue.cjs +7 -6
- package/dist/components/vue.cjs.map +1 -1
- package/dist/components/vue.d.cts +80 -76
- package/dist/components/vue.d.ts +80 -76
- package/dist/components/vue.js +7 -6
- package/dist/components/vue.js.map +1 -1
- package/dist/components/webgl.cjs +1 -1
- package/dist/components/webgl.d.cts +10 -0
- package/dist/components/webgl.d.ts +10 -0
- package/dist/components/webgl.js +2 -2
- package/dist/components.cjs +27 -5
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.cts +289 -108
- package/dist/components.d.ts +289 -108
- package/dist/components.js +9 -8
- package/dist/components.js.map +1 -1
- package/dist/components.umd.js +2 -2
- package/dist/components.umd.js.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.js +2 -2
- package/dist/index.umd.js.map +1 -1
- package/docs/ROADMAP.md +2 -2
- package/docs/accessibility.md +28 -0
- package/package.json +11 -1
- package/dist/chunks/base-BPG5zvex.js.map +0 -1
- package/dist/chunks/base-CXx7jZ-o.cjs.map +0 -1
|
@@ -27,7 +27,17 @@ interface ComponentsConfig {
|
|
|
27
27
|
* 1.25) on `<html>` for your own CSS. See `setMotionIntensity()`.
|
|
28
28
|
*/
|
|
29
29
|
motionIntensity?: MotionIntensity;
|
|
30
|
+
/**
|
|
31
|
+
* Motion-sensitivity level (v4.4), finer than reduced motion:
|
|
32
|
+
* `'full'` (default) · `'gentle'` (no spins, zooms, skews or parallax —
|
|
33
|
+
* translations and fades only, safe for vestibular disorders) ·
|
|
34
|
+
* `'minimal'` (fades only; components use their reduced-motion variants) ·
|
|
35
|
+
* `'static'` (no animation: every component shows its static alternative).
|
|
36
|
+
* See `setMotionSensitivity()` in `use-scroll-animate/components/a11y`.
|
|
37
|
+
*/
|
|
38
|
+
motionSensitivity?: MotionSensitivity;
|
|
30
39
|
}
|
|
40
|
+
type MotionSensitivity = 'full' | 'gentle' | 'minimal' | 'static';
|
|
31
41
|
type MotionIntensity = 'off' | 'low' | 'normal' | 'high';
|
|
32
42
|
/** Change global component settings (call before `define*()` for `injectStyles`). */
|
|
33
43
|
declare function configureComponents(options: ComponentsConfig): void;
|
|
@@ -69,7 +79,11 @@ declare function defineTypewriter(tag?: string): CustomElementConstructor | unde
|
|
|
69
79
|
* them in a cascade (pure CSS animation per unit, transform / opacity /
|
|
70
80
|
* filter only). Words never break across lines.
|
|
71
81
|
*
|
|
72
|
-
*
|
|
82
|
+
* 4.3: `Intl.Segmenter`-aware (emoji, CJK words), Arabic-script words are
|
|
83
|
+
* never split below the word, `by="lines"` reveals line by line, and `from`
|
|
84
|
+
* (`start` · `end` · `center` · `edges` · `random`) sets the cascade order.
|
|
85
|
+
*
|
|
86
|
+
* Attributes: `by` (`chars` | `words` | `lines`, default `chars`), `effect`
|
|
73
87
|
* (`rise` | `fade` | `blur` | `flip` | `pop`, default `rise`), `stagger`
|
|
74
88
|
* (ms between units, 28 for chars / 70 for words), `duration` (ms, 620),
|
|
75
89
|
* `delay` (ms, 0), `trigger` (`view` | `load` | `manual`, default `view`),
|
|
@@ -196,6 +210,155 @@ interface UsaScrollHighlightElement extends UsaElement {
|
|
|
196
210
|
}
|
|
197
211
|
declare function defineScrollHighlight(tag?: string): CustomElementConstructor | undefined;
|
|
198
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Where a step starts on a timeline:
|
|
215
|
+
* - a number: absolute time in ms
|
|
216
|
+
* - `'>'` (default): when the previous step ends · `'<'`: when it starts
|
|
217
|
+
* - `'+=200'` / `'-=200'`: after / overlapping the previous end
|
|
218
|
+
* - `'<+=100'`: 100ms after the previous step's start
|
|
219
|
+
* - `'intro'` / `'intro+=150'`: at (or relative to) a label
|
|
220
|
+
*/
|
|
221
|
+
type TimelinePosition = number | string;
|
|
222
|
+
interface TimelineStepOptions {
|
|
223
|
+
/** Duration in ms or a motion token name (`'fast'`, `'slow'`…; 4.2). Default: timeline default, 600. */
|
|
224
|
+
duration?: number | string;
|
|
225
|
+
/** CSS easing or a motion token name (`'emphasized'`, `'spring'`…; 4.2). Default `cubic-bezier(0.22, 1, 0.36, 1)`. */
|
|
226
|
+
easing?: string;
|
|
227
|
+
/** Start position, see `TimelinePosition`. */
|
|
228
|
+
at?: TimelinePosition;
|
|
229
|
+
/** ms between targets when the selector matches several elements. */
|
|
230
|
+
stagger?: number;
|
|
231
|
+
}
|
|
232
|
+
interface ScrubOptions {
|
|
233
|
+
/** Scroll offset (px) before the source's top reaches the viewport bottom where progress starts (JS engine only). */
|
|
234
|
+
offset?: number;
|
|
235
|
+
/** Smoothing 0–1 (0 = immediate, default 0). Smoothing needs the JS engine. */
|
|
236
|
+
smooth?: number;
|
|
237
|
+
/**
|
|
238
|
+
* 4.1: which progress source drives the timeline.
|
|
239
|
+
* - `'view'` (default): `source` moving through the viewport (CSS `ViewTimeline`, range `cover`).
|
|
240
|
+
* - `'scroll'`: the scroll position of `source` itself (a scroll container; CSS `ScrollTimeline`).
|
|
241
|
+
*/
|
|
242
|
+
source?: 'view' | 'scroll';
|
|
243
|
+
/** 4.1: `'auto'` (default) uses the browser's native scroll-driven animations when available, `'js'` forces the fallback. */
|
|
244
|
+
engine?: 'auto' | 'native' | 'js';
|
|
245
|
+
/** 4.1: scroll axis, `'block'` (default) · `'inline'` · `'x'` · `'y'`. */
|
|
246
|
+
axis?: 'block' | 'inline' | 'x' | 'y';
|
|
247
|
+
}
|
|
248
|
+
/** The function `scrub()` returns: call it to stop. `native` tells which engine runs it. */
|
|
249
|
+
interface ScrubHandle {
|
|
250
|
+
(): void;
|
|
251
|
+
/** `true` when the browser's ScrollTimeline / ViewTimeline drives it (compositor, no JS per frame). */
|
|
252
|
+
readonly native: boolean;
|
|
253
|
+
}
|
|
254
|
+
interface Timeline {
|
|
255
|
+
/** Total length in ms. */
|
|
256
|
+
readonly duration: number;
|
|
257
|
+
/** Label positions in ms. */
|
|
258
|
+
readonly labels: Readonly<Record<string, number>>;
|
|
259
|
+
/** Current playhead in ms. */
|
|
260
|
+
readonly time: number;
|
|
261
|
+
/** Add a step: animate `target` with keyframes or a preset name (`fade-up`, `scale`…). */
|
|
262
|
+
to(target: string | Element | Element[] | NodeList, frames: Keyframe[] | string, options?: TimelineStepOptions): Timeline;
|
|
263
|
+
/** Name a position (default: the current end). */
|
|
264
|
+
label(name: string, at?: TimelinePosition): Timeline;
|
|
265
|
+
/** Run `fn` when the playhead passes `at`. */
|
|
266
|
+
call(fn: () => void, at?: TimelinePosition): Timeline;
|
|
267
|
+
/** Play forwards from the playhead (from 0 when at the end). Resolves at the end. */
|
|
268
|
+
play(from?: TimelinePosition): Promise<void>;
|
|
269
|
+
/** Play backwards to 0. */
|
|
270
|
+
reverse(): Promise<void>;
|
|
271
|
+
pause(): Timeline;
|
|
272
|
+
/** Jump to a time (ms) or label. */
|
|
273
|
+
seek(to: TimelinePosition): Timeline;
|
|
274
|
+
/** Get or set progress 0–1. */
|
|
275
|
+
progress(p?: number): number;
|
|
276
|
+
/**
|
|
277
|
+
* Tie progress to scroll: `source` moving through the viewport (or, with
|
|
278
|
+
* `{ source: 'scroll' }`, a scroll container's own position). Runs on native
|
|
279
|
+
* ScrollTimeline / ViewTimeline when available (and no `smooth`, `offset`,
|
|
280
|
+
* `call()` cues or `onUpdate` need JS), else on a rAF-throttled listener.
|
|
281
|
+
* Returns a stop function with a `native` flag.
|
|
282
|
+
*/
|
|
283
|
+
scrub(source: Element, options?: ScrubOptions): ScrubHandle;
|
|
284
|
+
/** Stop and drop every animation (elements keep their last frame). */
|
|
285
|
+
cancel(): void;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* `splitText()` (4.3) — split an element's text into characters, words and / or
|
|
290
|
+
* lines, ready for per-unit choreography with `timeline()`.
|
|
291
|
+
*
|
|
292
|
+
* - Grapheme- and word-aware via `Intl.Segmenter` when available: emoji and
|
|
293
|
+
* combining marks stay whole; Chinese / Japanese / Korean text is split into
|
|
294
|
+
* real words (or one unit per character without `Segmenter`).
|
|
295
|
+
* - RTL aware: Arabic-script text (cursive, letters join) is never split
|
|
296
|
+
* below the word, so shaping is preserved; Hebrew and other RTL scripts
|
|
297
|
+
* split per character. Units stay in logical (reading) order.
|
|
298
|
+
* - Nested inline markup (`<em>`, `<a>`, `<br>`) is preserved.
|
|
299
|
+
* - Accessible: the original text stays available to assistive tech via a
|
|
300
|
+
* visually hidden copy; the split spans are `aria-hidden`.
|
|
301
|
+
*/
|
|
302
|
+
type SplitBy = 'char' | 'word' | 'line';
|
|
303
|
+
interface SplitTextOptions {
|
|
304
|
+
/** What to split into: `'char'`, `'word'`, `'line'` or several, e.g. `['word', 'line']`. Default `'char'` (words are always wrapped too). */
|
|
305
|
+
by?: SplitBy | SplitBy[] | string;
|
|
306
|
+
/** Locale for `Intl.Segmenter` (default: the element's `lang`, else the document's). */
|
|
307
|
+
locale?: string;
|
|
308
|
+
/** Class prefix (default `usa-split`): units get `usa-split-char` / `-word` / `-line`. */
|
|
309
|
+
className?: string;
|
|
310
|
+
}
|
|
311
|
+
interface SplitResult {
|
|
312
|
+
chars: HTMLElement[];
|
|
313
|
+
words: HTMLElement[];
|
|
314
|
+
lines: HTMLElement[];
|
|
315
|
+
/** `'rtl'` or `'ltr'` — the direction the split ran in. */
|
|
316
|
+
direction: 'ltr' | 'rtl';
|
|
317
|
+
/** Re-measure lines (call after a resize or font load). */
|
|
318
|
+
relayout(): HTMLElement[];
|
|
319
|
+
/** Restore the original markup. */
|
|
320
|
+
revert(): void;
|
|
321
|
+
}
|
|
322
|
+
/** Scripts whose letters join (splitting them would break shaping). */
|
|
323
|
+
declare const JOINING_SCRIPT: RegExp;
|
|
324
|
+
/** Grapheme clusters of `s` (emoji / combining marks stay whole). */
|
|
325
|
+
declare function graphemes(s: string, locale?: string): string[];
|
|
326
|
+
/**
|
|
327
|
+
* Word-ish tokens of `s`, whitespace kept as separate tokens. CJK text is
|
|
328
|
+
* segmented into words with `Intl.Segmenter`, or per character without it.
|
|
329
|
+
*/
|
|
330
|
+
declare function words(s: string, locale?: string): string[];
|
|
331
|
+
declare function splitText(el: HTMLElement, options?: SplitTextOptions): SplitResult;
|
|
332
|
+
type SplitFrom = 'start' | 'end' | 'center' | 'edges' | 'random';
|
|
333
|
+
/** Order indices `0…n-1` by choreography: from the start, end, center outwards, edges inwards, or random (seeded). */
|
|
334
|
+
declare function splitOrder(n: number, from?: SplitFrom, seed?: number): number[];
|
|
335
|
+
interface SplitTimelineOptions extends SplitTextOptions {
|
|
336
|
+
/** Which units to animate (default: the finest in `by`). */
|
|
337
|
+
unit?: 'char' | 'word' | 'line';
|
|
338
|
+
/** Timeline preset name or keyframes (default `'fade-up'`). */
|
|
339
|
+
preset?: string | Keyframe[];
|
|
340
|
+
/** ms between units (default 30 for chars, 80 for words, 140 for lines). */
|
|
341
|
+
stagger?: number;
|
|
342
|
+
/** Duration per unit in ms or a motion token name (default 500). */
|
|
343
|
+
duration?: number | string;
|
|
344
|
+
easing?: string;
|
|
345
|
+
/** Choreography order (default `'start'` — reading order, also for RTL). */
|
|
346
|
+
from?: SplitFrom;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Split `el` and build a `timeline()` with one step per unit — play it,
|
|
350
|
+
* `scrub()` it with scroll, `reverse()` or `seek()` it.
|
|
351
|
+
*
|
|
352
|
+
* ```ts
|
|
353
|
+
* const { timeline: tl } = splitTimeline(h1, { by: 'char', preset: 'blur', from: 'center' });
|
|
354
|
+
* tl.play();
|
|
355
|
+
* ```
|
|
356
|
+
*/
|
|
357
|
+
declare function splitTimeline(el: HTMLElement, options?: SplitTimelineOptions): {
|
|
358
|
+
split: SplitResult;
|
|
359
|
+
timeline: Timeline;
|
|
360
|
+
};
|
|
361
|
+
|
|
199
362
|
/**
|
|
200
363
|
* use-scroll-animate/components/text — text effects.
|
|
201
364
|
* `<usa-typewriter>`, `<usa-split-text>`, `<usa-scramble>`, `<usa-counter>`,
|
|
@@ -220,5 +383,5 @@ declare global {
|
|
|
220
383
|
}
|
|
221
384
|
}
|
|
222
385
|
|
|
223
|
-
export { configureComponents, defineCounter, defineGlitch, defineGradientText, defineHandwriting, defineScramble, defineScrollHighlight, defineShimmerText, defineSplitText, defineTextComponents, defineTextRotate, defineTypewriter, defineWaveText, easeOutExpo, prefersReducedMotion, scrambleFrame };
|
|
224
|
-
export type { ComponentsConfig, UsaCounterElement, UsaElement, UsaGlitchElement, UsaGradientTextElement, UsaHandwritingElement, UsaScrambleElement, UsaScrollHighlightElement, UsaShimmerTextElement, UsaSplitTextElement, UsaTextRotateElement, UsaTypewriterElement, UsaWaveTextElement };
|
|
386
|
+
export { JOINING_SCRIPT, configureComponents, defineCounter, defineGlitch, defineGradientText, defineHandwriting, defineScramble, defineScrollHighlight, defineShimmerText, defineSplitText, defineTextComponents, defineTextRotate, defineTypewriter, defineWaveText, easeOutExpo, graphemes, prefersReducedMotion, scrambleFrame, splitOrder, splitText, splitTimeline, words as splitWords };
|
|
387
|
+
export type { ComponentsConfig, SplitBy, SplitFrom, SplitResult, SplitTextOptions, SplitTimelineOptions, UsaCounterElement, UsaElement, UsaGlitchElement, UsaGradientTextElement, UsaHandwritingElement, UsaScrambleElement, UsaScrollHighlightElement, UsaShimmerTextElement, UsaSplitTextElement, UsaTextRotateElement, UsaTypewriterElement, UsaWaveTextElement };
|
package/dist/components/text.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
1
|
+
import { k as defineElement, q as srText, h as caf, n as now, r as raf, l as EASE_OUT, E as EASE_SPRING, f as clamp } from '../chunks/base-BtJDNCB6.js';
|
|
2
|
+
export { d as configureComponents, p as prefersReducedMotion } from '../chunks/base-BtJDNCB6.js';
|
|
3
|
+
import { t as timeline } from '../chunks/core-DS1nrx5L.js';
|
|
4
|
+
import './tokens.js';
|
|
3
5
|
|
|
4
6
|
var css$5 = "usa-typewriter{white-space:pre-wrap}usa-typewriter .usa-tw-caret{display:inline-block;width:var(--usa-caret-width,0.08em);height:1.05em;margin-left:0.06em;vertical-align:-0.12em;background:var(--usa-caret-color,currentColor);animation:usa-caret 1.06s steps(1) infinite}usa-typewriter[data-typing] .usa-tw-caret{animation:none}usa-typewriter[data-no-cursor] .usa-tw-caret{display:none}@keyframes usa-caret{50%{opacity:0}}@media (prefers-reduced-motion:reduce){usa-typewriter .usa-tw-caret,usa-shimmer-text{animation:none}}";
|
|
5
7
|
|
|
@@ -120,12 +122,255 @@ function defineTypewriter(tag = 'usa-typewriter') {
|
|
|
120
122
|
|
|
121
123
|
var css$4 = "usa-split-text .usa-split-word{display:inline-block;white-space:nowrap}usa-split-text .usa-split-unit{display:inline-block;white-space:pre}usa-split-text[data-state=\"hidden\"] .usa-split-unit{opacity:0}usa-split-text[data-state=\"play\"] .usa-split-unit{animation:usa-split-rise var(--usa-split-duration,620ms) cubic-bezier(0.22,1,0.36,1) both;animation-delay:calc(var(--usa-split-delay,0ms) + var(--i,0) * var(--usa-split-stagger,28ms))}usa-split-text[effect=\"fade\"][data-state=\"play\"] .usa-split-unit{animation-name:usa-split-fade}usa-split-text[effect=\"blur\"][data-state=\"play\"] .usa-split-unit{animation-name:usa-split-blur}usa-split-text[effect=\"flip\"][data-state=\"play\"] .usa-split-unit{animation-name:usa-split-flip;transform-origin:50% 100%}usa-split-text[effect=\"pop\"][data-state=\"play\"] .usa-split-unit{animation-name:usa-split-pop;animation-timing-function:cubic-bezier(0.34,1.56,0.64,1)}@keyframes usa-split-rise{from{opacity:0;transform:translate3d(0,0.6em,0)}to{opacity:1;transform:none}}@keyframes usa-split-fade{from{opacity:0}to{opacity:1}}@keyframes usa-split-blur{from{opacity:0;filter:blur(8px)}to{opacity:1;filter:none}}@keyframes usa-split-flip{from{opacity:0;transform:perspective(500px) rotateX(-80deg)}to{opacity:1;transform:none}}@keyframes usa-split-pop{from{opacity:0;transform:scale(0.3)}to{opacity:1;transform:none}}@media (prefers-reduced-motion:reduce){usa-split-text .usa-split-unit{animation:none !important;opacity:1 !important}}";
|
|
122
124
|
|
|
125
|
+
const CJK = /[\u2E80-\u2FFF\u3000-\u303F\u3040-\u30FF\u3100-\u312F\u3130-\u318F\u31A0-\u31FF\u3400-\u4DBF\u4E00-\u9FFF\uAC00-\uD7AF\uF900-\uFAFF\uFF00-\uFFEF]/;
|
|
126
|
+
/** Scripts whose letters join (splitting them would break shaping). */
|
|
127
|
+
const JOINING_SCRIPT = /[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]/;
|
|
128
|
+
const RTL = /[\u0590-\u08FF\uFB1D-\uFDFF\uFE70-\uFEFF]/;
|
|
129
|
+
const SR = 'position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0';
|
|
130
|
+
function segmenter(locale, granularity) {
|
|
131
|
+
const S = globalThis.Intl?.Segmenter;
|
|
132
|
+
if (typeof S !== 'function')
|
|
133
|
+
return null;
|
|
134
|
+
try {
|
|
135
|
+
return new S(locale || undefined, { granularity });
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/** Grapheme clusters of `s` (emoji / combining marks stay whole). */
|
|
142
|
+
function graphemes(s, locale) {
|
|
143
|
+
const seg = segmenter(locale, 'grapheme');
|
|
144
|
+
if (seg)
|
|
145
|
+
return Array.from(seg.segment(s), (x) => x.segment);
|
|
146
|
+
return Array.from(s);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Word-ish tokens of `s`, whitespace kept as separate tokens. CJK text is
|
|
150
|
+
* segmented into words with `Intl.Segmenter`, or per character without it.
|
|
151
|
+
*/
|
|
152
|
+
function words(s, locale) {
|
|
153
|
+
const seg = segmenter(locale, 'word');
|
|
154
|
+
const out = [];
|
|
155
|
+
if (seg) {
|
|
156
|
+
for (const x of seg.segment(s)) {
|
|
157
|
+
const last = out[out.length - 1];
|
|
158
|
+
// glue punctuation onto the previous word so it never starts a line alone
|
|
159
|
+
if (!x.isWordLike && !/^\s+$/.test(x.segment) && last && !/^\s+$/.test(last))
|
|
160
|
+
out[out.length - 1] = last + x.segment;
|
|
161
|
+
else
|
|
162
|
+
out.push(x.segment);
|
|
163
|
+
}
|
|
164
|
+
return out;
|
|
165
|
+
}
|
|
166
|
+
for (const part of s.split(/(\s+)/)) {
|
|
167
|
+
if (!part)
|
|
168
|
+
continue;
|
|
169
|
+
if (/^\s+$/.test(part) || !CJK.test(part))
|
|
170
|
+
out.push(part);
|
|
171
|
+
else {
|
|
172
|
+
let buf = '';
|
|
173
|
+
for (const ch of Array.from(part)) {
|
|
174
|
+
if (CJK.test(ch)) {
|
|
175
|
+
if (buf)
|
|
176
|
+
out.push(buf), (buf = '');
|
|
177
|
+
out.push(ch);
|
|
178
|
+
}
|
|
179
|
+
else if (/[\p{P}]/u.test(ch) && out.length)
|
|
180
|
+
out[out.length - 1] += ch;
|
|
181
|
+
else
|
|
182
|
+
buf += ch;
|
|
183
|
+
}
|
|
184
|
+
if (buf)
|
|
185
|
+
out.push(buf);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return out;
|
|
189
|
+
}
|
|
190
|
+
function dirOf(el, text) {
|
|
191
|
+
const attr = el.closest?.('[dir]')?.getAttribute('dir');
|
|
192
|
+
if (attr === 'rtl' || attr === 'ltr')
|
|
193
|
+
return attr;
|
|
194
|
+
try {
|
|
195
|
+
const d = getComputedStyle(el).direction;
|
|
196
|
+
if (d === 'rtl')
|
|
197
|
+
return 'rtl';
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
/* no layout */
|
|
201
|
+
}
|
|
202
|
+
return RTL.test(text) && !/[A-Za-z]/.test(text.replace(RTL, '')) ? 'rtl' : 'ltr';
|
|
203
|
+
}
|
|
204
|
+
function splitText(el, options = {}) {
|
|
205
|
+
const by = new Set((Array.isArray(options.by) ? options.by : String(options.by ?? 'char').split(/[\s,]+/)).filter(Boolean));
|
|
206
|
+
const cls = options.className || 'usa-split';
|
|
207
|
+
const locale = options.locale || el.closest?.('[lang]')?.getAttribute('lang') || undefined;
|
|
208
|
+
const original = Array.from(el.childNodes).map((n) => n.cloneNode(true));
|
|
209
|
+
const text = el.textContent || '';
|
|
210
|
+
const direction = dirOf(el, text);
|
|
211
|
+
const chars = [];
|
|
212
|
+
const wordEls = [];
|
|
213
|
+
let lines = [];
|
|
214
|
+
const doc = el.ownerDocument;
|
|
215
|
+
const span = (c, t) => {
|
|
216
|
+
const s = doc.createElement('span');
|
|
217
|
+
s.className = c;
|
|
218
|
+
if (t !== undefined)
|
|
219
|
+
s.textContent = t;
|
|
220
|
+
return s;
|
|
221
|
+
};
|
|
222
|
+
const splitNode = (node) => {
|
|
223
|
+
if (node.nodeType === 3) {
|
|
224
|
+
const frag = doc.createDocumentFragment();
|
|
225
|
+
for (const w of words(node.nodeValue || '', locale)) {
|
|
226
|
+
if (/^\s+$/.test(w)) {
|
|
227
|
+
frag.append(doc.createTextNode(w));
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const wordEl = span(`${cls}-word`);
|
|
231
|
+
wordEl.style.display = 'inline-block';
|
|
232
|
+
wordEl.style.whiteSpace = 'nowrap';
|
|
233
|
+
wordEl.dataset.index = String(wordEls.length);
|
|
234
|
+
wordEls.push(wordEl);
|
|
235
|
+
// Arabic script joins its letters: keep the word whole (shaping), even in char mode.
|
|
236
|
+
if (by.has('char') && !JOINING_SCRIPT.test(w)) {
|
|
237
|
+
for (const g of graphemes(w, locale)) {
|
|
238
|
+
const c = span(`${cls}-char`, g);
|
|
239
|
+
c.style.display = 'inline-block';
|
|
240
|
+
c.dataset.index = String(chars.length);
|
|
241
|
+
chars.push(c);
|
|
242
|
+
wordEl.append(c);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
wordEl.textContent = w;
|
|
247
|
+
if (by.has('char')) {
|
|
248
|
+
wordEl.dataset.whole = '';
|
|
249
|
+
chars.push(wordEl);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
frag.append(wordEl);
|
|
253
|
+
}
|
|
254
|
+
node.parentNode.replaceChild(frag, node);
|
|
255
|
+
}
|
|
256
|
+
else if (node.nodeType === 1 && !/^(BR|SCRIPT|STYLE|SVG|IMG)$/i.test(node.tagName)) {
|
|
257
|
+
Array.from(node.childNodes).forEach(splitNode);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
const wrap = doc.createElement('span');
|
|
261
|
+
wrap.setAttribute('aria-hidden', 'true');
|
|
262
|
+
wrap.className = `${cls}-body`;
|
|
263
|
+
original.forEach((n) => wrap.append(n.cloneNode(true)));
|
|
264
|
+
Array.from(wrap.childNodes).forEach(splitNode);
|
|
265
|
+
const sr = span(`${cls}-sr`, text.replace(/\s+/g, ' ').trim());
|
|
266
|
+
sr.setAttribute('style', SR);
|
|
267
|
+
el.replaceChildren(sr, wrap);
|
|
268
|
+
el.setAttribute('data-split', [...by].join(' '));
|
|
269
|
+
if (direction === 'rtl')
|
|
270
|
+
el.setAttribute('data-split-dir', 'rtl');
|
|
271
|
+
const relayout = () => {
|
|
272
|
+
// unwrap old lines
|
|
273
|
+
for (const l of lines)
|
|
274
|
+
l.replaceWith(...Array.from(l.childNodes));
|
|
275
|
+
lines = [];
|
|
276
|
+
if (!by.has('line') || !wordEls.length)
|
|
277
|
+
return lines;
|
|
278
|
+
const groups = [];
|
|
279
|
+
let lastTop = null;
|
|
280
|
+
for (const w of wordEls) {
|
|
281
|
+
const top = Math.round(w.offsetTop);
|
|
282
|
+
if (lastTop === null || Math.abs(top - lastTop) > 2)
|
|
283
|
+
groups.push([]);
|
|
284
|
+
groups[groups.length - 1].push(w);
|
|
285
|
+
lastTop = top;
|
|
286
|
+
}
|
|
287
|
+
groups.forEach((g, i) => {
|
|
288
|
+
// only group words that share a parent (inline markup keeps its words)
|
|
289
|
+
const parent = g[0].parentNode;
|
|
290
|
+
const same = g.filter((w) => w.parentNode === parent);
|
|
291
|
+
const line = span(`${cls}-line`);
|
|
292
|
+
line.style.display = 'inline-block';
|
|
293
|
+
line.dataset.index = String(i);
|
|
294
|
+
parent.insertBefore(line, same[0]);
|
|
295
|
+
let n = same[0];
|
|
296
|
+
const end = same[same.length - 1];
|
|
297
|
+
while (n) {
|
|
298
|
+
const next = n.nextSibling;
|
|
299
|
+
line.append(n);
|
|
300
|
+
if (n === end)
|
|
301
|
+
break;
|
|
302
|
+
n = next;
|
|
303
|
+
}
|
|
304
|
+
// keep the space between lines outside the line box
|
|
305
|
+
lines.push(line);
|
|
306
|
+
});
|
|
307
|
+
return lines;
|
|
308
|
+
};
|
|
309
|
+
relayout();
|
|
310
|
+
return {
|
|
311
|
+
chars,
|
|
312
|
+
words: wordEls,
|
|
313
|
+
get lines() {
|
|
314
|
+
return lines;
|
|
315
|
+
},
|
|
316
|
+
direction,
|
|
317
|
+
relayout,
|
|
318
|
+
revert() {
|
|
319
|
+
el.replaceChildren(...original.map((n) => n.cloneNode(true)));
|
|
320
|
+
el.removeAttribute('data-split');
|
|
321
|
+
el.removeAttribute('data-split-dir');
|
|
322
|
+
},
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
/** Order indices `0…n-1` by choreography: from the start, end, center outwards, edges inwards, or random (seeded). */
|
|
326
|
+
function splitOrder(n, from = 'start', seed = 1) {
|
|
327
|
+
const idx = Array.from({ length: n }, (_, i) => i);
|
|
328
|
+
const mid = (n - 1) / 2;
|
|
329
|
+
if (from === 'end')
|
|
330
|
+
return idx.map((i) => n - 1 - i);
|
|
331
|
+
if (from === 'center')
|
|
332
|
+
return idx.map((i) => Math.round(Math.abs(i - mid) * 2) / 2);
|
|
333
|
+
if (from === 'edges')
|
|
334
|
+
return idx.map((i) => Math.round((mid - Math.abs(i - mid)) * 2) / 2);
|
|
335
|
+
if (from === 'random') {
|
|
336
|
+
let s = seed;
|
|
337
|
+
const r = () => ((s = (s * 16807) % 2147483647) / 2147483647);
|
|
338
|
+
const shuffled = idx.slice().sort(() => r() - 0.5);
|
|
339
|
+
const rank = [];
|
|
340
|
+
shuffled.forEach((v, i) => (rank[v] = i));
|
|
341
|
+
return rank;
|
|
342
|
+
}
|
|
343
|
+
return idx;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Split `el` and build a `timeline()` with one step per unit — play it,
|
|
347
|
+
* `scrub()` it with scroll, `reverse()` or `seek()` it.
|
|
348
|
+
*
|
|
349
|
+
* ```ts
|
|
350
|
+
* const { timeline: tl } = splitTimeline(h1, { by: 'char', preset: 'blur', from: 'center' });
|
|
351
|
+
* tl.play();
|
|
352
|
+
* ```
|
|
353
|
+
*/
|
|
354
|
+
function splitTimeline(el, options = {}) {
|
|
355
|
+
const split = splitText(el, options);
|
|
356
|
+
const by = Array.isArray(options.by) ? options.by : String(options.by ?? 'char').split(/[\s,]+/);
|
|
357
|
+
const unit = options.unit || (by.includes('char') ? 'char' : by.includes('word') ? 'word' : 'line');
|
|
358
|
+
const units = unit === 'char' ? split.chars : unit === 'word' ? split.words : split.lines;
|
|
359
|
+
const stagger = options.stagger ?? (unit === 'char' ? 30 : unit === 'word' ? 80 : 140);
|
|
360
|
+
const order = splitOrder(units.length, options.from);
|
|
361
|
+
const tl = timeline({ defaults: { duration: options.duration ?? 500, easing: options.easing } });
|
|
362
|
+
units.forEach((u, i) => tl.to(u, options.preset || 'fade-up', { at: order[i] * stagger }));
|
|
363
|
+
tl.seek(0);
|
|
364
|
+
return { split, timeline: tl };
|
|
365
|
+
}
|
|
366
|
+
|
|
123
367
|
function defineSplitText(tag = 'usa-split-text') {
|
|
124
368
|
return defineElement(tag, (Base) => class UsaSplitText extends Base {
|
|
125
369
|
constructor() {
|
|
126
370
|
super(...arguments);
|
|
127
371
|
this._source = null;
|
|
128
372
|
this._timer = 0;
|
|
373
|
+
this._steps = 0;
|
|
129
374
|
}
|
|
130
375
|
static get observedAttributes() {
|
|
131
376
|
return ['by', 'text'];
|
|
@@ -137,28 +382,50 @@ function defineSplitText(tag = 'usa-split-text') {
|
|
|
137
382
|
if (this._source === null)
|
|
138
383
|
this._source = this.getAttribute('text') ?? (this.textContent || '').replace(/\s+/g, ' ').trim();
|
|
139
384
|
const text = this.getAttribute('text') ?? this._source;
|
|
140
|
-
const
|
|
385
|
+
const mode = this.str('by', 'chars');
|
|
386
|
+
const byWords = mode !== 'chars';
|
|
387
|
+
const lang = this.closest('[lang]')?.getAttribute('lang') || undefined;
|
|
141
388
|
const frag = document.createDocumentFragment();
|
|
142
389
|
frag.append(srText(text));
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (
|
|
390
|
+
const units = [];
|
|
391
|
+
for (const word of words(text, lang)) {
|
|
392
|
+
if (/^\s+$/.test(word)) {
|
|
146
393
|
frag.append(' ');
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
147
396
|
const wordEl = document.createElement('span');
|
|
148
397
|
wordEl.className = 'usa-split-word';
|
|
149
398
|
wordEl.setAttribute('aria-hidden', 'true');
|
|
150
|
-
const parts = byWords ? [word] :
|
|
399
|
+
const parts = byWords || JOINING_SCRIPT.test(word) ? [word] : graphemes(word, lang);
|
|
151
400
|
for (const part of parts) {
|
|
152
401
|
const u = document.createElement('span');
|
|
153
402
|
u.className = 'usa-split-unit';
|
|
154
403
|
u.textContent = part;
|
|
155
|
-
|
|
404
|
+
units.push(u);
|
|
156
405
|
wordEl.append(u);
|
|
157
406
|
}
|
|
158
407
|
frag.append(wordEl);
|
|
159
|
-
}
|
|
408
|
+
}
|
|
160
409
|
this.replaceChildren(frag);
|
|
161
|
-
|
|
410
|
+
// cascade order: per unit, or per line for by="lines"
|
|
411
|
+
let rank = units.map((_, k) => k);
|
|
412
|
+
if (mode === 'lines') {
|
|
413
|
+
let line = -1;
|
|
414
|
+
let top = null;
|
|
415
|
+
rank = units.map((u) => {
|
|
416
|
+
const t = Math.round(u.parentElement.offsetTop);
|
|
417
|
+
if (top === null || Math.abs(t - top) > 2)
|
|
418
|
+
line++;
|
|
419
|
+
top = t;
|
|
420
|
+
return line;
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
const groups = Math.max(0, ...rank) + 1;
|
|
424
|
+
const order = splitOrder(groups, this.str('from', 'start'));
|
|
425
|
+
units.forEach((u, k) => u.style.setProperty('--i', String(order[rank[k]] ?? 0)));
|
|
426
|
+
const i = Math.max(0, ...units.map((_, k) => order[rank[k]] ?? 0)) + 1;
|
|
427
|
+
this._steps = i;
|
|
428
|
+
this.style.setProperty('--usa-split-stagger', `${this.num('stagger', mode === 'lines' ? 140 : byWords ? 70 : 28)}ms`);
|
|
162
429
|
this.style.setProperty('--usa-split-duration', `${this.num('duration', 620)}ms`);
|
|
163
430
|
this.style.setProperty('--usa-split-delay', `${this.num('delay', 0)}ms`);
|
|
164
431
|
this.style.setProperty('--usa-split-count', String(i));
|
|
@@ -192,8 +459,8 @@ function defineSplitText(tag = 'usa-split-text') {
|
|
|
192
459
|
this.setAttribute('data-state', 'hidden');
|
|
193
460
|
void this.offsetWidth;
|
|
194
461
|
this.setAttribute('data-state', 'play');
|
|
195
|
-
const
|
|
196
|
-
const total = this.num('delay', 0) + this.num('duration', 620) + Math.max(0,
|
|
462
|
+
const by = this.str('by', 'chars');
|
|
463
|
+
const total = this.num('delay', 0) + this.num('duration', 620) + Math.max(0, this._steps - 1) * this.num('stagger', by === 'lines' ? 140 : by === 'words' ? 70 : 28);
|
|
197
464
|
this._timer = setTimeout(() => {
|
|
198
465
|
this.setAttribute('data-state', 'shown');
|
|
199
466
|
this.emit('complete');
|
|
@@ -677,5 +944,5 @@ function defineTextComponents() {
|
|
|
677
944
|
defineScrollHighlight();
|
|
678
945
|
}
|
|
679
946
|
|
|
680
|
-
export { defineCounter, defineGlitch, defineGradientText, defineHandwriting, defineScramble, defineScrollHighlight, defineShimmerText, defineSplitText, defineTextComponents, defineTextRotate, defineTypewriter, defineWaveText, easeOutExpo, scrambleFrame };
|
|
947
|
+
export { JOINING_SCRIPT, defineCounter, defineGlitch, defineGradientText, defineHandwriting, defineScramble, defineScrollHighlight, defineShimmerText, defineSplitText, defineTextComponents, defineTextRotate, defineTypewriter, defineWaveText, easeOutExpo, graphemes, scrambleFrame, splitOrder, splitText, splitTimeline, words as splitWords };
|
|
681
948
|
//# sourceMappingURL=text.js.map
|