kuinetic 0.1.4 → 0.2.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.
Files changed (124) hide show
  1. package/README.md +93 -10
  2. package/dist/esm/advanced/index.mjs +4727 -0
  3. package/dist/esm/chunk-AEQGC2KM.mjs +189 -0
  4. package/dist/esm/chunk-MQSNMYRP.mjs +9332 -0
  5. package/dist/esm/chunk-RVTYN6NZ.mjs +3446 -0
  6. package/dist/esm/chunk-U43BX5O2.mjs +2984 -0
  7. package/dist/esm/core/index.mjs +16 -9
  8. package/dist/esm/effects/index.mjs +19 -4
  9. package/dist/esm/index.mjs +17 -10
  10. package/dist/kuinetic.advanced.js +5070 -0
  11. package/dist/kuinetic.advanced.min.js +749 -0
  12. package/dist/kuinetic.all.js +15147 -6039
  13. package/dist/kuinetic.all.min.js +2 -0
  14. package/dist/kuinetic.css +2801 -318
  15. package/dist/kuinetic.js +15147 -6038
  16. package/dist/kuinetic.min.css +1 -0
  17. package/dist/kuinetic.min.js +7 -0
  18. package/dist/types/3d/angles.d.ts +30 -0
  19. package/dist/types/3d/flattening.d.ts +49 -0
  20. package/dist/types/3d/index.d.ts +36 -0
  21. package/dist/types/3d/model-3d.d.ts +115 -0
  22. package/dist/types/3d/register.d.ts +26 -0
  23. package/dist/types/3d/webgl.d.ts +24 -0
  24. package/dist/types/advanced/audio.d.ts +322 -0
  25. package/dist/types/advanced/base.d.ts +233 -0
  26. package/dist/types/advanced/camera-3d.d.ts +236 -0
  27. package/dist/types/advanced/fluid-cursor.d.ts +149 -0
  28. package/dist/types/advanced/gl-utils.d.ts +52 -0
  29. package/dist/types/advanced/glsl.d.ts +60 -0
  30. package/dist/types/advanced/index.d.ts +23 -0
  31. package/dist/types/advanced/particles.d.ts +108 -0
  32. package/dist/types/advanced/scenes.d.ts +207 -0
  33. package/dist/types/advanced/shaders.d.ts +972 -0
  34. package/dist/types/browser/boot.d.ts +72 -0
  35. package/dist/types/core/activation.d.ts +227 -2
  36. package/dist/types/core/animator.d.ts +446 -11
  37. package/dist/types/core/attrs.d.ts +9 -0
  38. package/dist/types/core/breakpoints.d.ts +260 -0
  39. package/dist/types/core/bundles.d.ts +71 -0
  40. package/dist/types/core/callback.d.ts +64 -0
  41. package/dist/types/core/capabilities.d.ts +47 -0
  42. package/dist/types/core/channels.d.ts +120 -2
  43. package/dist/types/core/cloak-selectors.d.ts +47 -0
  44. package/dist/types/core/compile.d.ts +207 -2
  45. package/dist/types/core/control.d.ts +218 -0
  46. package/dist/types/core/declarations.d.ts +128 -0
  47. package/dist/types/core/derived/aggregate.d.ts +43 -0
  48. package/dist/types/core/derived/book.d.ts +30 -0
  49. package/dist/types/core/derived/diagnostics.d.ts +51 -0
  50. package/dist/types/core/derived/group-gate.d.ts +62 -0
  51. package/dist/types/core/derived/install.d.ts +110 -0
  52. package/dist/types/core/derived/late-matches.d.ts +19 -0
  53. package/dist/types/core/derived/types.d.ts +108 -0
  54. package/dist/types/core/easing.d.ts +57 -0
  55. package/dist/types/core/element-config.d.ts +47 -6
  56. package/dist/types/core/element-size.d.ts +23 -0
  57. package/dist/types/core/event-sources.d.ts +39 -0
  58. package/dist/types/core/flip.d.ts +34 -1
  59. package/dist/types/core/gesture.d.ts +12 -0
  60. package/dist/types/core/host-facts.d.ts +155 -0
  61. package/dist/types/core/index.d.ts +7 -3
  62. package/dist/types/core/owned-styles.d.ts +129 -2
  63. package/dist/types/core/params.d.ts +48 -0
  64. package/dist/types/core/parse.d.ts +28 -1
  65. package/dist/types/core/register-into.d.ts +29 -0
  66. package/dist/types/core/registry.d.ts +10 -3
  67. package/dist/types/core/repeat.d.ts +91 -0
  68. package/dist/types/core/sequence.d.ts +126 -0
  69. package/dist/types/core/stagger-config.d.ts +199 -0
  70. package/dist/types/core/stagger-keys.d.ts +63 -0
  71. package/dist/types/core/stagger.d.ts +187 -6
  72. package/dist/types/core/style-plan.d.ts +21 -16
  73. package/dist/types/core/target.d.ts +128 -0
  74. package/dist/types/core/threshold-reachability.d.ts +45 -0
  75. package/dist/types/core/time-scale.d.ts +9 -0
  76. package/dist/types/core/toggle-actions.d.ts +141 -0
  77. package/dist/types/core/travel.d.ts +59 -0
  78. package/dist/types/core/types.d.ts +699 -13
  79. package/dist/types/core/unquoted-selectors.d.ts +53 -0
  80. package/dist/types/effects/carousel/drag.d.ts +80 -0
  81. package/dist/types/effects/carousel/index.d.ts +149 -0
  82. package/dist/types/effects/catalog/discrete.d.ts +13 -0
  83. package/dist/types/effects/catalog/interaction-proximity.d.ts +24 -0
  84. package/dist/types/effects/catalog/interaction-reveal.d.ts +248 -0
  85. package/dist/types/effects/catalog/interaction-shared.d.ts +42 -0
  86. package/dist/types/effects/catalog/interaction-states.d.ts +68 -0
  87. package/dist/types/effects/catalog/materials.d.ts +67 -0
  88. package/dist/types/effects/catalog/media-shared.d.ts +3 -2
  89. package/dist/types/effects/catalog/numbers-shared.d.ts +8 -5
  90. package/dist/types/effects/catalog/shared.d.ts +1 -1
  91. package/dist/types/effects/catalog/text-shared.d.ts +86 -10
  92. package/dist/types/effects/catalog/transforms.d.ts +13 -0
  93. package/dist/types/effects/catalog/view-transitions.d.ts +13 -0
  94. package/dist/types/effects/forms/primitives.d.ts +15 -4
  95. package/dist/types/effects/gestures/index.d.ts +53 -0
  96. package/dist/types/effects/index.d.ts +5 -0
  97. package/dist/types/effects/layout/presets.d.ts +35 -0
  98. package/dist/types/effects/motion-path/index.d.ts +35 -0
  99. package/dist/types/effects/scroll-mechanics/params.d.ts +23 -0
  100. package/dist/types/effects/scroll-mechanics/presets.d.ts +51 -0
  101. package/dist/types/effects/scroll-mechanics/section-index.d.ts +47 -0
  102. package/dist/types/effects/shared.d.ts +124 -4
  103. package/dist/types/effects/step-index.d.ts +121 -0
  104. package/dist/types/effects/step-marking.d.ts +41 -33
  105. package/dist/types/effects/svg/icon-parts.d.ts +23 -0
  106. package/dist/types/effects/three-d/flip-parts.d.ts +46 -0
  107. package/dist/types/effects/tween/index.d.ts +57 -0
  108. package/dist/types/effects/tween/properties.d.ts +107 -0
  109. package/dist/types/effects/tween/waypoints.d.ts +106 -0
  110. package/dist/types/showcase/compare.d.ts +4 -0
  111. package/dist/types/showcase/device-frame.d.ts +3 -0
  112. package/dist/types/showcase/hotspots.d.ts +3 -0
  113. package/dist/types/showcase/index.d.ts +30 -0
  114. package/dist/types/showcase/lightbox.d.ts +3 -0
  115. package/dist/types/showcase/media-source.d.ts +13 -0
  116. package/dist/types/showcase/modal-shell.d.ts +22 -0
  117. package/dist/types/showcase/scroll-story.d.ts +4 -0
  118. package/dist/types/showcase/shared.d.ts +47 -0
  119. package/dist/types/showcase/slideshow.d.ts +3 -0
  120. package/dist/types/showcase/slow-mo.d.ts +5 -0
  121. package/package.json +14 -7
  122. package/dist/esm/chunk-7WMNPIOZ.mjs +0 -4883
  123. package/dist/esm/chunk-JT4PZL3A.mjs +0 -773
  124. package/dist/esm/chunk-TJDDIQRG.mjs +0 -1290
@@ -0,0 +1,68 @@
1
+ import type { Preset, Primitive } from '../../core/types.js';
2
+ /**
3
+ * The press state. One primitive, one preset today — the split is the composability the name
4
+ * promises.
5
+ *
6
+ * A future `glass-press`, `press-tilt`, or a brand's own pressed treatment is a second `Preset`
7
+ * row on this same primitive plus its own selector in `interaction.css`: it inherits the
8
+ * `--kui-press-duration/-delay/-ease` namespace (those are keyed on the *primitive* id by
9
+ * `registry.ts`'s `namespaceTiming`), the parameter schema, the reduced-motion policy, and the
10
+ * channel claim, and adds only the declarations that differ. That is the same
11
+ * one-primitive-many-names shape the rest of the catalog uses — 130 of the catalog's names are
12
+ * aliases of this kind — and it is why `press` is the primitive rather than `press-depth`.
13
+ *
14
+ * ### Channels: `scale` and `shadow`, and specifically not `translate`
15
+ *
16
+ * A channel claim is not documentation, it is a veto: `compile.ts`'s `resolveComposition` *drops*
17
+ * every effect after the first when two claims overlap, so an over-broad claim silently deletes
18
+ * an effect an author wrote. `translate` is the claim this primitive could plausibly have made and
19
+ * must not: `lift` owns `translate` and `data-kui="lift, press-depth"` — raise on hover, sink on
20
+ * press — is the single most useful pairing this effect has. Claiming `translate` would make the
21
+ * compiler refuse it and drop the press entirely.
22
+ *
23
+ * That is not a loophole; the sink genuinely does not need `translate`. A press reads as depth
24
+ * through the scale and through the shadow collapsing toward the surface, which is how the two
25
+ * dominant design systems build the same state. What it costs is real and worth naming:
26
+ * `lift-shadow` and `border-glow` both own `shadow`, so composing either with `press-depth` is
27
+ * refused. That refusal is correct rather than incidental — all three write `box-shadow` on the
28
+ * same box in overlapping states, and whichever rule came later in `interaction.css` would win the
29
+ * whole property.
30
+ */
31
+ export declare const PRESS_PRIMITIVES: Primitive[];
32
+ export declare const PRESS_PRESETS: Preset[];
33
+ /**
34
+ * Collective hover: the container recedes, except for the member you are pointing at.
35
+ *
36
+ * ### Why the name is `group-dim`
37
+ *
38
+ * The effect goes on the *container* — `data-kui="group-dim"` on the `<ul>`, the grid, the row —
39
+ * and reads whatever children are already there, so a page using it writes no structural CSS of
40
+ * its own. A name has to be true on the element it is written on, and that rules out the obvious
41
+ * `siblings-dim`/`dim-siblings` spelling: written on a `<ul>`, "siblings" names the `<ul>`'s own
42
+ * siblings, which are the one set of elements this effect never touches. `group-dim` names the
43
+ * container's contents, which is what actually dims.
44
+ *
45
+ * `group-` is also a prefix with room in it. This is the first collective effect in the catalog,
46
+ * and a `group-blur` or `group-desaturate` is the same mechanism with a different property; they
47
+ * would share this primitive's channel and read as one family in the catalog's group column.
48
+ *
49
+ * ### Channels: `group`, a channel of its own
50
+ *
51
+ * Every other channel in the model names properties on the element `data-kui-fx` sits on. This one
52
+ * paints its *children's* `opacity`, which is a different box, and filing it under `opacity` would
53
+ * make the compiler refuse `data-kui="fade-in, group-dim"` — a container that fades itself in and
54
+ * dims its children on hover — even though the two never touch the same box. Declaring the box set
55
+ * the effect actually owns is what keeps the veto pointed at the real collision: a second
56
+ * `group-*` effect on the same container.
57
+ *
58
+ * `requiresOwnSubtree` follows directly and is not optional. `target:` relocates `data-kui-fx` onto
59
+ * whatever a selector matches, and a rule that reaches `> *` compiles to silence when it lands on
60
+ * an element with a different shape beneath it. `compile.ts`'s `liftTarget` refuses to relocate a
61
+ * preset that declares this, and `css-requires-own-subtree.test.ts` re-derives the true set from
62
+ * the shipped stylesheets rather than trusting the flag.
63
+ */
64
+ export declare const GROUP_PRIMITIVES: Primitive[];
65
+ export declare const GROUP_PRESETS: Preset[];
66
+ /** Both state families, for `interaction.ts`'s catalog-wide exports. */
67
+ export declare const STATE_PRIMITIVES: Primitive[];
68
+ export declare const STATE_PRESETS: Preset[];
@@ -0,0 +1,67 @@
1
+ import type { Preset, Primitive } from '../../core/types.js';
2
+ import type { Registry } from '../../core/registry.js';
3
+ export declare const MATERIALS_PRIMITIVES: Primitive[];
4
+ /**
5
+ * The preset name and the primitive id are both `glass` — the same string in two different
6
+ * namespaces, which `registry.ts` keeps as separate maps (`resolve()` looks the authored name up
7
+ * in `presets`, then follows `preset.primitive` into `primitives`), so there is no collision to
8
+ * worry about. Plenty of the catalog already does this (`fade-open`, `cursor-follow`,
9
+ * `rotate-static`, and more) — a preset sharing its primitive's id is the common case, not the
10
+ * exception.
11
+ *
12
+ * Keeping them as two rows rather than one still buys composability: a distinctly-named preset —
13
+ * someone's own house style, say — is a second `Preset` row here plus its own selector in
14
+ * `glass.css`, inheriting the parameter schema, the channel claim, the reduced-motion policy and
15
+ * the perf class rather than restating them. `blur:` and `tint:` already cover the
16
+ * heavier-blur / different-tint variations that used to motivate that point, so those are
17
+ * `glass blur:32px` or `glass tint:navy`, not a second name — but the mechanism is still here for
18
+ * a preset that genuinely needs one. Same one-primitive-many-names shape the rest of the catalog
19
+ * uses, and the reason `press` — not `press-depth` — is the primitive next door.
20
+ *
21
+ * No `transitions` field. `Preset.transitions` describes properties this preset eases on its own
22
+ * host box, and nothing here eases: the surface is painted once and does not change. A composed
23
+ * `press-depth` brings its own `scale`/`box-shadow` segments, which is exactly the division of
24
+ * labour this family is built around.
25
+ *
26
+ * ### No `phase` field either, and this one took real thought
27
+ *
28
+ * None of the four {@link EffectPhase} values (`core/types.ts`) is honest about what `glass` does,
29
+ * and forcing one on would be worse than the unphased default it falls back to:
30
+ *
31
+ * - **Not `entrance`/`exit`.** Both describe a one-shot animation that starts from a hidden or
32
+ * displaced state and *releases* the channel the moment it finishes — that release is what makes
33
+ * `fade-up, lift` safe (see `channels.ts`'s `INDEPENDENT_PHASES`). `glass` never plays anything
34
+ * and never releases `background`/`backdrop`: `prepareGlass` above is a no-op, and the CSS rule
35
+ * in `glass.css` is an unconditional declaration with no keyframe at all. There is no from-state
36
+ * to hide and nothing to hand back to the cascade.
37
+ * - **Not `idle`.** `idle` is specifically an *unbounded animation loop* — a running clock that
38
+ * never yields, tracked in CSS as `--kui-fx-<name>-iterations: infinite` (see `ambient-tint`'s
39
+ * presets in `ambient.ts`, which also claim `background` and are exactly this). `glass` has no
40
+ * clock and no iteration count; it is not looping, it simply is.
41
+ * - **Not `state`.** `state` means the channel is held by a condition the *visitor* changes
42
+ * (`:hover`, `[aria-expanded]`) and released once that condition goes away. `glass`'s hold is
43
+ * unconditional from the moment it activates (`defaultActivation: 'load'`) and never releases —
44
+ * there is no visitor action that ends it.
45
+ *
46
+ * So `glass` holds `background`/`backdrop` exactly the way `idle` does — forever, with no turn to
47
+ * hand off — without being an animation, which is the one fact every {@link EffectPhase} value
48
+ * assumes. Declaring `idle` anyway was the tempting shortcut, since it happens to be inert against
49
+ * today's `INDEPENDENT_PHASES` table (`idle` is not a member of either exempted pair, so it would
50
+ * conflict with everything exactly as unphased does) — but "inert today" is not the same as
51
+ * "true", and a false claim sitting on the record is exactly what costs someone a real bug the day
52
+ * that table grows a third exemption. Leaving `phase` undeclared is the honest answer *and* the
53
+ * correct one operationally: an unphased claim conflicts with any other claimant on
54
+ * `background`/`backdrop`, which is exactly right, because anything else painting on that channel
55
+ * *forever* — another material, an `idle` ambient loop like `scanline` — is a genuine, permanent
56
+ * fight over the same property, not two effects taking turns. See
57
+ * `test/catalog-phase-materials.test.ts` for the compiled proof.
58
+ */
59
+ export declare const MATERIALS_PRESETS: Preset[];
60
+ /**
61
+ * Register catalog section S (materials) into a registry.
62
+ *
63
+ * @param registry - Registry to populate.
64
+ * @returns The same registry, for chaining.
65
+ * @complexity O(n) time in registered primitives and presets; O(1) extra space.
66
+ */
67
+ export declare function registerMaterials(registry: Registry): Registry;
@@ -35,9 +35,10 @@ export declare function slatOrder(index: number, count: number, from: SlatFrom):
35
35
  * Resolve the authored `angle:` to degrees, falling back to whatever `axis:` names.
36
36
  *
37
37
  * `angle:` is the general parameter and `axis:` the two-keyword shorthand that predates it, so an
38
- * authored angle wins and an absent one reads the axis. Both `45` and `45deg` are accepted, as are
38
+ * authored angle wins and an absent one reads the axis. `45`, `45d` and `45deg` are accepted, as are
39
39
  * `turn` and `rad`, because an author who writes an angle should not have to remember which of the
40
- * three this particular parameter takes.
40
+ * three this particular parameter takes. `core/params.ts` takes the same three spellings for every
41
+ * other `type: 'angle'` parameter.
41
42
  *
42
43
  * Normalised to `[0, 180)`: a band at 200° is the same set of bands as one at 20°, only numbered
43
44
  * from the other end, and collapsing that here means the geometry below never has to think about
@@ -10,8 +10,10 @@ export interface CountLayers {
10
10
  /** `aria-hidden` node the ticking display writes to on every frame. */
11
11
  decorative: HTMLElement;
12
12
  /**
13
- * Visually-hidden twin. The caller must write this exactly once, on completion — never
14
- * mid-tick — so a screen reader is told the final value and nothing in between.
13
+ * Visually-hidden twin. Holds the final value from the moment it is installed and never
14
+ * changes again — the number is content, not a status update, so there is nothing to announce
15
+ * mid-count (design.md §12's "no aria-live spam from counters"). The caller writes it once, at
16
+ * install, from the same `to`/`format` the tween itself will settle on.
15
17
  */
16
18
  srOnly: HTMLElement;
17
19
  /**
@@ -23,12 +25,13 @@ export interface CountLayers {
23
25
  }
24
26
  /**
25
27
  * Replace an element's content with an `aria-hidden` ticking display plus a visually-hidden twin,
26
- * both empty until the caller populates them, so a counter's mid-flight text is never read aloud
27
- * and its `aria-live` region changes exactly once.
28
+ * both empty until the caller populates them. The decorative layer is free to tick every frame;
29
+ * the SR-only twin is meant to be written once, with the counter's final value, and never touched
30
+ * again — a counter is content, not a live status, so it carries no `aria-live`.
28
31
  *
29
32
  * @param el - Element whose content is being taken over. Assumed to hold no meaningful children.
30
33
  * @param doc - Document to create nodes in, rather than the ambient global.
31
- * @returns The decorative node to tick, the SR-only node to finalize once, and a cleanup.
34
+ * @returns The decorative node to tick, the SR-only node to populate once, and a cleanup.
32
35
  * @complexity O(1) time and space.
33
36
  * @overallScore 100
34
37
  */
@@ -1,2 +1,2 @@
1
1
  /** Re-exported for same-directory catalog siblings; the canonical definitions live in `effects/shared.ts` — neutral ground for every effect category, not just `catalog/`. */
2
- export { cssPrimitive, TIMELINE_AGNOSTIC, TRIGGER_DELAY_PARAM } from '../shared.js';
2
+ export { cssPrimitive, TIMELINE_AGNOSTIC, TRIGGER_DELAY_PARAM, withTimingContract, } from '../shared.js';
@@ -1,9 +1,30 @@
1
- import type { Cleanup, EffectParams } from '../../core/types.js';
1
+ import type { Cleanup, EffectParams, EffectSpec, EffectVariant, ParameterSchema } from '../../core/types.js';
2
2
  /** How `split-text` breaks a string into decorative pieces. */
3
3
  export type SplitUnit = 'chars' | 'words' | 'lines';
4
+ /**
5
+ * Selector for the elements a content-mutating text/number effect must never remove from the DOM:
6
+ * a link, a form control, anything focusable or already interactive. Every primitive that takes
7
+ * over an element's text (`installSplitLayers`, `installCountLayers`, `word-cycler`) flattens
8
+ * markup into plain text or a captured-and-replaced subtree, and a link caught inside that is gone
9
+ * from the page for as long as the effect runs — for a one-shot effect that never settles back,
10
+ * permanently. Losing a link costs SEO; losing a focusable control is worse, because it costs a
11
+ * keyboard user access to it.
12
+ */
13
+ export declare const INTERACTIVE_DESCENDANT = "a[href], button, input, select, textarea, [tabindex], [contenteditable], details, summary, iframe, audio[controls], video[controls]";
14
+ /**
15
+ * Whether `el` contains an interactive descendant a content-mutating effect would otherwise
16
+ * remove or flatten.
17
+ *
18
+ * @param el - Host element the effect is about to take over.
19
+ * @complexity O(n) time in descendant count (native `querySelector`); O(1) space.
20
+ * @overallScore 100
21
+ */
22
+ export declare function hasInteractiveDescendant(el: Element): boolean;
4
23
  export interface SplitLayers {
5
24
  /** `aria-hidden` container the caller populates with decorative markup or text. */
6
25
  decorative: HTMLElement;
26
+ /** The accessible reading copy while the decorative pieces are animating. */
27
+ srOnly: HTMLElement;
7
28
  /** The element's text at the moment splitting began, trimmed for display. */
8
29
  originalText: string;
9
30
  /**
@@ -19,7 +40,9 @@ export interface SplitLayers {
19
40
  * screen reader ever seeing an incomplete or garbled read of the content.
20
41
  *
21
42
  * @param el - Element whose text is being taken over. Assumed to hold plain text (no children) —
22
- * every DOM-surgery primitive in this module shares that constraint.
43
+ * every DOM-surgery primitive in this module shares that constraint. Callers are expected to
44
+ * have already checked {@link hasInteractiveDescendant} and bailed out if it is true; this
45
+ * function itself does not guard, so a caller that skips the check still flattens a link.
23
46
  * @param doc - Document to create nodes in, rather than the ambient global, so callers can point
24
47
  * this at a test document.
25
48
  * @returns The decorative layer to populate, the captured text, and a cleanup.
@@ -128,24 +151,28 @@ export declare function createStepRunner(win: Window, options: StepRunOptions):
128
151
  */
129
152
  export declare function appendCharSpans(container: Element, doc: Document, text: string): HTMLElement[];
130
153
  /**
131
- * Split text into one span per word, leaving the whitespace and punctuation between them as plain
132
- * text nodes so natural line-wrapping and spacing survive untouched.
154
+ * Split text into animated words. Opening punctuation joins the next word; other punctuation
155
+ * joins the preceding word. A punctuation run without a word gets its own item. Only whitespace
156
+ * stays outside the items, preserving natural wrapping and spacing.
133
157
  *
134
158
  * @complexity O(n) time and space in segment count.
135
159
  * @overallScore 100
136
160
  */
137
161
  export declare function appendWordSpans(container: Element, doc: Document, text: string): HTMLElement[];
138
162
  /**
139
- * Split text into one span per visual line.
140
- *
141
- * Lines are not a text property — they only exist after wrapping — so this measures word spans
142
- * already laid out in the live DOM, then regroups their nodes (words and the whitespace between
143
- * them) into per-line containers.
144
- *
163
+ * Split text into measured visual lines.
145
164
  * @complexity O(n) time and space in word-span count.
146
165
  * @overallScore 100
147
166
  */
148
167
  export declare function appendLineSpans(container: Element, doc: Document, text: string): HTMLElement[];
168
+ /**
169
+ * Put existing words and whitespace back into natural flow, measure, and wrap the new lines.
170
+ * The offsetTop reads in wrapMeasuredLines finish before it writes any line wrappers.
171
+ *
172
+ * @complexity O(n) time and space in word-span count.
173
+ * @overallScore 100
174
+ */
175
+ export declare function rewrapLineSpans(container: Element, doc: Document): HTMLElement[];
149
176
  /**
150
177
  * Split text by the requested unit.
151
178
  *
@@ -187,3 +214,52 @@ export declare const SCRAMBLE_CHARSETS: Record<string, string>;
187
214
  * @overallScore 100
188
215
  */
189
216
  export declare function scrambledFrame(graphemes: string[], resolved: number, charset: string, random: () => number): string;
217
+ /**
218
+ * The generic variable-font axis: `data-kui="var-axis axis:GRAD from:0 to:150"`.
219
+ *
220
+ * `var-weight`/`var-width`/`var-slant` cover the three axes CSS gave a high-level property to.
221
+ * Everything else a real type system exposes — Fraunces' `SOFT` and `WONK`, Recursive's `CASL`,
222
+ * `MONO` and `CRSV`, Roboto Flex's `GRAD` and `XOPQ`, `opsz` on almost everything modern — has no
223
+ * property of its own and can only be reached through `font-variation-settings`. Three hardcoded
224
+ * names cannot grow to meet that; a tag parameter can.
225
+ *
226
+ * `from`/`to` are deliberately unbounded numbers. Axis ranges are a property of the *font*, not of
227
+ * CSS — `wght` is 1–1000, `opsz` is typically 8–144, `GRAD` runs −200 to 150, `CASL` is 0–1 — so
228
+ * any `minimum`/`maximum` this schema could state would be wrong for most axes and would reject
229
+ * legitimate values. The renderer already handles the out-of-range case correctly: CSS Fonts 4
230
+ * clamps an axis value to the range the font declares, so an over-large `to:` reads as "all the way
231
+ * to that axis's maximum" rather than as a broken declaration.
232
+ *
233
+ * The defaults are weight-shaped (100 → 900) because `wght` is far and away the most common axis
234
+ * and a bare `data-kui="var-axis"` should do something recognisable. On a font with no `wght` axis
235
+ * that is a no-op, which is also what an unsupported axis should look like.
236
+ */
237
+ export declare const varAxisParams: ParameterSchema;
238
+ /**
239
+ * Turn an authored `axis:GRAD` into the quoted CSS string the keyframe needs, and into a schema
240
+ * that accepts exactly that one value.
241
+ *
242
+ * This is the whole reason `var-axis` can be generic while every parameter in the library is
243
+ * validated against a closed grammar. `ParamSpec` has no "four-character font tag" type, and the
244
+ * types that *are* open (`text`) are dropped before the stylesheet precisely because they are open
245
+ * — so a tag cannot reach CSS as an ordinary parameter at all. What `variantFor` adds is the
246
+ * ability to decide the grammar *per spec*: the tag is checked against {@link AXIS_TAG} here, then
247
+ * declared as the sole legal keyword, so `resolveParams` still runs its full escape screen over a
248
+ * value it can only accept if it is identical to the one already vetted. Two independent gates, and
249
+ * neither is loosened — this narrows the accepted set to one string rather than widening it.
250
+ *
251
+ * The `tween` primitive uses the same mechanism for the same class of reason: one authored key
252
+ * (`x:'0,100,40'`) expanding into parameters the primitive could not have declared statically. See
253
+ * `EffectVariant.schema` in `core/types.ts`.
254
+ *
255
+ * A rejected tag is *dropped*, not substituted. Rewriting it to the default would animate the
256
+ * wrong axis under a name the author did not write, which is the silent-wrong-behaviour outcome
257
+ * this codebase treats as worse than doing nothing; dropping it leaves the `var()` fallback in
258
+ * force and the warning is the only account of what happened.
259
+ *
260
+ * @param spec - The authored spec, untouched — `compile` recompiles the same value on every rescan.
261
+ * @param warn - Diagnostic sink, so a mistyped tag is named rather than silently ignored.
262
+ * @returns A variant carrying the quoted tag and its one-value schema, or an empty refinement.
263
+ * @complexity O(p) time and space in the spec's parameter count, for the copy.
264
+ */
265
+ export declare function varAxisVariant(spec: EffectSpec, warn: (message: string) => void): EffectVariant;
@@ -0,0 +1,13 @@
1
+ import type { Preset, Primitive } from '../../core/types.js';
2
+ import type { Registry } from '../../core/registry.js';
3
+ export declare const TRANSFORMS_PRIMITIVES: Primitive[];
4
+ export declare const TRANSFORMS_PRESETS: Preset[];
5
+ /**
6
+ * Register catalog section R (static transforms) into a registry.
7
+ *
8
+ * @param registry - Registry to populate.
9
+ * @returns The same registry, for chaining.
10
+ * @complexity O(n) time in registered primitives and presets; O(1) extra space.
11
+ * @overallScore 100
12
+ */
13
+ export declare function registerTransforms(registry: Registry): Registry;
@@ -0,0 +1,13 @@
1
+ import type { Registry } from '../../core/registry.js';
2
+ import type { Preset, Primitive } from '../../core/types.js';
3
+ export declare const VIEW_TRANSITION_PRIMITIVES: Primitive[];
4
+ export declare const VIEW_TRANSITION_PRESETS: Preset[];
5
+ /**
6
+ * Register the View Transitions family (catalog section L).
7
+ *
8
+ * @param registry - Registry to populate.
9
+ * @returns The same registry, for chaining.
10
+ * @complexity O(n) time in registered primitives and presets.
11
+ * @overallScore 100
12
+ */
13
+ export declare function registerViewTransitions(registry: Registry): Registry;
@@ -15,13 +15,24 @@ export declare const VALIDATE_CHECK_PRIMITIVE: Primitive;
15
15
  export declare function computeStrength(value: string): number;
16
16
  export declare const STRENGTH_METER_PRIMITIVE: Primitive;
17
17
  export declare const RANGE_FILL_PRIMITIVE: Primitive;
18
+ export { nextStep, prevStep, clampStep, countSteps, delegateControls } from '../step-index.js';
19
+ export type { ControlGroup } from '../step-index.js';
18
20
  /**
19
- * Advance a step index, wrapping back to 0 — pure, so the wrap rule is assertable without a DOM.
21
+ * The only member of the family that is not `reducedMotion: 'disable'`, and the difference is the
22
+ * whole widget.
20
23
  *
21
- * @complexity O(1) time and space.
22
- * @overallScore 100
24
+ * Under `disable` the animator never activates the instance, so the deferred setup never runs: no
25
+ * control listeners, no index published, no slide marked. A visitor who asked for reduced motion
26
+ * got a deck whose arrows and dots did nothing at all — not a calmer carousel, a broken one.
27
+ * Reduced motion is a request to suppress animation, never to withdraw function.
28
+ *
29
+ * Nothing is lost by shortening instead, because this primitive animates nothing itself. It writes
30
+ * `data-kui-step` / `--kui-step` on the container and `data-kui-step-state` / `--kui-offset` on the
31
+ * steps; every transition or animation keyed off those belongs to the page or to forms.css, which
32
+ * is exactly the layer base.css's `[data-kui-rm]` block reaches — including, since this changed,
33
+ * the shipped stepper segments' own 200ms fade. The index still moves and the right slide is still
34
+ * marked; the travel between them is what gets taken away.
23
35
  */
24
- export declare function nextStep(step: number, total: number): number;
25
36
  export declare const STEP_PROGRESS_PRIMITIVE: Primitive;
26
37
  export type SubmitStage = 'idle' | 'loading' | 'done';
27
38
  /**
@@ -5,6 +5,59 @@ import type { Registry } from '../../core/registry.js';
5
5
  *
6
6
  * The drag family differs only in where a release goes: back to origin (elastic), onward with
7
7
  * momentum (throwable), or nowhere (plain drag). That is one primitive with two booleans.
8
+ *
9
+ * All thirteen declare `phase: 'state'`, and none declares `transitions` (nothing here renders
10
+ * through CSS transitions to derive it from — every primitive in this file is `renderer:
11
+ * 'javascript'`), so this is the whole story rather than a shortcut around it. `compile.ts`'s
12
+ * `phaseOf` treats an undeclared phase as a fact nothing is known about, which conflicts with
13
+ * everything including itself — before this, `data-kui="fade-up on:enter, drag"` hit that wall:
14
+ * both segments claim `translate`, `draggable`'s renderer disqualifies the additive rescue (only
15
+ * `css-keyframes` tracks qualify), no combo is registered for the pair, and the compiler silently
16
+ * kept only `fade-up` — an entrance plus a drag on one element, an entirely ordinary request,
17
+ * did nothing. Interaction-reveal's four presets (`masked-label-swap*`, `hover-intent`) shipped
18
+ * the identical gap hours earlier; this is the same bug in its second family, not a new one.
19
+ *
20
+ * The test throughout is *when does this effect hold the channel it claims*, not *is this a
21
+ * press*: `'state'` means "governed by a condition the visitor changes, released — or resting at
22
+ * a value it holds until changed again — once that condition is gone," which is exactly
23
+ * `entrance`'s counterpart (an entrance plays once and hands the channel to whatever the cascade
24
+ * says next; see `EffectPhase`'s doc comment). Every one of these primitives writes an inline
25
+ * style or attribute imperatively rather than through a CSS animation track, so there is no
26
+ * `to`-only or `from`-only keyframe to inspect — the fact instead is about *when the gesture
27
+ * runs*:
28
+ *
29
+ * - `draggable` (`drag`, `drag-x`, `drag-y`, `drag-inertia`, `throwable`, `elastic-pull`,
30
+ * `rubber-band`, `snap-back`) writes `translate` only from `pointerdown` through however long
31
+ * the release takes to resolve — instantly for the plain three, a spring settle for the
32
+ * inertia pair, a spring return to zero for the elastic three (see `primitives.ts`'s `settle`).
33
+ * The inertia pair is the one worth pausing on, because it keeps moving *after* the pointer
34
+ * lifts: `drag-inertia`/`throwable` are not "done interacting" the instant `onEnd` fires, but
35
+ * they are also not `idle` — `idle` in this catalog means an unbounded loop that never yields
36
+ * the channel (`--kui-fx-<name>-iterations: infinite`), and a decelerating throw is bounded and
37
+ * does stop. It is the same one continuous gesture instance settling on its own terms, not a
38
+ * second trigger, so it is still `state` and not a fourth thing this enum has no room for.
39
+ * Every member rests at whatever `translate` it ends on — dropped in place for the plain three,
40
+ * carried by momentum for the inertia pair, snapped to zero for the elastic three — and holds
41
+ * there via the same inline style until the next drag picks it up again. None of that is an
42
+ * `entrance`: there is no hidden from-state and no `cloak`, just an idle rest value between
43
+ * gestures.
44
+ * - `swipeable` (`swipe`, `swipe-x`) and `pressable` (`long-press`) claim the primitive-level
45
+ * `'state'` *channel* (a bucket for "writes an attribute, not a CSS property" — see
46
+ * `gestures/primitives.ts`'s `channels: ['state']` — a name that collides with this same-named
47
+ * `EffectPhase` value only in spelling, not in meaning). `long-press` is the clean case: it
48
+ * flips `data-kui-pressed` on the hold and back off on release, precisely the `:active`-shaped
49
+ * "held only while the visitor is doing something" case the `state` phase doc describes.
50
+ * `swipe`/`swipe-x` are a discrete, already-over gesture by the time `data-kui-swipe` is
51
+ * written (`onSwipe` fires once, at recognition) rather than a continuous hold — but the same
52
+ * family precedent already covers a discrete, dwell-triggered write: `hover-intent` (
53
+ * `interaction-reveal.ts`) sets its class once, on a timer firing during a hover, and keeps it
54
+ * until the pointer actually leaves, and that shipped `phase: 'state'`. A swipe's attribute is
55
+ * the same shape — written once by an interaction, held until the same primitive changes it
56
+ * again — and not an `entrance`, `exit`, or unbounded `idle`.
57
+ * - `magnetic` (`magnetic`, `magnetic-snap`) writes `translate` continuously while the pointer is
58
+ * within `radius`, and springs back toward zero the moment it leaves — proximity rather than a
59
+ * press, but still a condition the visitor's own movement drives and un-drives, the same
60
+ * `state` shape as a `:hover` rule with a transition.
8
61
  */
9
62
  export declare const GESTURE_PRESETS: Preset[];
10
63
  /**
@@ -2,10 +2,15 @@ import { Registry } from '../core/registry.js';
2
2
  export { PRIMITIVES, PRESETS, COMBOS, registerCore } from './catalog/core.js';
3
3
  export { registerGestures } from './gestures/index.js';
4
4
  export { registerLayout } from './layout/index.js';
5
+ export { registerMotionPath } from './motion-path/index.js';
5
6
  export { registerScrollMechanics } from './scroll-mechanics/index.js';
6
7
  export { registerSvg } from './svg/index.js';
7
8
  export { registerThreeD } from './three-d/index.js';
9
+ export { registerCarousel } from './carousel/index.js';
8
10
  export { registerCatalog } from './catalog/index.js';
11
+ export { registerTween, TWEEN_PRESETS, TWEEN_PRIMITIVES } from './tween/index.js';
12
+ export { registerViewTransitions } from './catalog/view-transitions.js';
13
+ export { registerShowcase } from '../showcase/index.js';
9
14
  /**
10
15
  * A registry with the full catalog registered.
11
16
  *
@@ -5,5 +5,40 @@ import type { Preset } from '../../core/types.js';
5
5
  * The four `flip-*` names are the same primitive: what differs is why the children moved, and the
6
6
  * engine neither knows nor needs to. They exist as separate names because authors think in terms
7
7
  * of "I filtered a list", not "I mutated a child list".
8
+ *
9
+ * Phase (see `EffectPhase`, `core/channels.ts`): eight of these nine are deliberately left
10
+ * unphased, and that was checked against the actual mechanism rather than assumed from "FLIP looks
11
+ * like an entrance."
12
+ *
13
+ * `flip-container` (the seven `flip-*`/`grid-to-list`/`masonry-reflow`/`expand-to-modal` names) and
14
+ * `auto-height` genuinely *do* release the channels they animate: `flip.ts`'s `runDeltas` and
15
+ * `primitives.ts`'s `animateHeight` both compile `fill: delay > 0 ? 'backwards' : 'none'` — never
16
+ * `'forwards'`/`'both'` — so once a reorder or a height tween finishes, the Web Animation's
17
+ * contribution is gone and the element's `translate`/`scale`/`height` reads whatever the DOM's own
18
+ * layout or the page's own stylesheet says next. Nothing here holds a rest value the way
19
+ * `gestures.ts`'s `draggable` does. That is exactly the `entrance` shape ("plays once from a
20
+ * displaced from-state, then yields the channel") — but it cannot be declared `entrance`, because
21
+ * that value's contract in this catalog is narrower than the English word: `test
22
+ * /composition-phase.test.ts` asserts, catalog-wide, that every `phase: 'entrance'` preset backs
23
+ * onto a real `@keyframes` block with no closing `to`/`100%` step, to prove the release is real.
24
+ * These primitives are `renderer: 'javascript'` and animate through ad hoc `Element.animate()`
25
+ * calls built at runtime — there is no `@keyframes kui-flip-reorder` (or `-accordion-height`) in
26
+ * any shipped stylesheet for that test to find, so declaring `entrance` here would fail an
27
+ * off-limits invariant rather than extend it. None of the other three values fit either: not
28
+ * `idle` (every run is bounded — `deltas.length === 0` even short-circuits to a no-op), not `state`
29
+ * (nothing is held between calls to persist), not `exit` (nothing departs; a reorder settles back
30
+ * into a normal, still-visible layout position). Left unphased, these eight keep conflicting with
31
+ * a same-channel neighbour exactly as they did before this change — a real gap, but the one the
32
+ * mechanism actually supports today rather than a value chosen to make the enum feel complete.
33
+ *
34
+ * `tab-indicator-slide` (`flip-indicator`) is the one exception, and the reason is a real
35
+ * difference in the primitive, not a different reading of the same one. `prepareIndicator` writes
36
+ * the indicator's landing spot with `ctx.style.set('translate', ...)` — a **persistent** inline
37
+ * style — before it ever calls `engine.play()`; the FLIP animation only smooths the visible trip
38
+ * between the old inline value and the new one, and hands back to that same persisted inline style
39
+ * on finish (fill: none resolves to identity, which is "no offset from the inline value already
40
+ * written"). So the rest state genuinely is held, between moves, by an inline write the same way
41
+ * `draggable`'s drop position is — the exact `state` shape `gestures/index.ts` established, just
42
+ * driven by "the followed target changed" instead of a pointer.
8
43
  */
9
44
  export declare const LAYOUT_PRESETS: Preset[];
@@ -0,0 +1,35 @@
1
+ import type { Preset, Primitive } from '../../core/types.js';
2
+ import type { Registry } from '../../core/registry.js';
3
+ /**
4
+ * The one primitive. `perfClass: 'compositor'` is the shared default and is honest here:
5
+ * `offset-distance` is a transform-like property that Chromium animates off the main thread.
6
+ *
7
+ * `reducedMotion` is left at `cssPrimitive`'s `'shorten'`, which resolves to a 1ms animation — the
8
+ * element arrives at the end of its path with no motion to see, which is the right answer for a
9
+ * vestibular trigger and the same treatment `slide-out-up` and every other travel-and-stay effect
10
+ * already gets. `'disable'` would be worse, not better: it leaves the animation off entirely, so
11
+ * an entrance like `path-swoop` would sit permanently at its *start* — off to the lower left of
12
+ * where it belongs. `base.css` additionally drops `offset-path` under `disable` and in print, so
13
+ * a composed effect that forces that policy cannot strand a path-driven element off its mark.
14
+ */
15
+ export declare const MOTION_PATH_PRIMITIVES: Primitive[];
16
+ /**
17
+ * The named paths.
18
+ *
19
+ * §6.4 of the parity outline makes shipping these non-negotiable, and it is right to: a primitive
20
+ * that only works once the author has opened a vector editor is not a catalog entry, it is an API.
21
+ * Every name below animates on its own, with nothing but `data-kui="path-arc"`.
22
+ *
23
+ * The geometry is written to read at a glance — round numbers, control points that are obviously
24
+ * symmetrical — because these are the first thing anyone will copy and edit.
25
+ */
26
+ export declare const MOTION_PATH_PRESETS: Preset[];
27
+ /**
28
+ * Register the motion-path family.
29
+ *
30
+ * @param registry - Registry to populate.
31
+ * @returns The same registry, for chaining.
32
+ * @complexity O(n) time in the number of primitives and presets.
33
+ * @overallScore 100
34
+ */
35
+ export declare function registerMotionPath(registry: Registry): Registry;
@@ -0,0 +1,23 @@
1
+ import type { ParameterSchema } from '../../core/types.js';
2
+ /**
3
+ * The parameter schemas shared across the scroll-mechanics primitives.
4
+ *
5
+ * Extracted from `primitives.ts`, which sits close enough to the 400-line cap that adding one
6
+ * parameter pushes it over. They belong together anyway: these are the knobs more than one
7
+ * primitive declares, and the whole point of declaring them once is that `pin` and `media-scrub`
8
+ * cannot drift apart on what a hold means.
9
+ */
10
+ export declare const distanceParam: ParameterSchema;
11
+ /**
12
+ * Shared by every primitive that has to hold still while its scroll range passes.
13
+ *
14
+ * `pin` and `media-scrub` were solving the same problem twice from opposite ends. The pin declared
15
+ * both of these and did the work; the scrub declared neither and left it to the page, which is why
16
+ * `demo/scroll.html` carried a `.scrub-stage` whose entire job was `height: 260vh` and a
17
+ * `.scrub-viewport` whose entire job was `position: sticky`. Two hand-written boxes to restate
18
+ * something the library already knew, because `distance:` was on the attribute the whole time.
19
+ *
20
+ * Declaring it once means the two cannot drift, and it is what lets a scrub become one attribute
21
+ * with no wrapper at all.
22
+ */
23
+ export declare const stickyParams: ParameterSchema;
@@ -2,5 +2,56 @@ import type { Preset } from '../../core/types.js';
2
2
  /**
3
3
  * Scroll-mechanics names. Eleven names over six primitives — the same alias-table shape the
4
4
  * entrance matrix uses, so adding a variant stays a data change.
5
+ *
6
+ * **Every name here declares `phase: 'idle'`.** This is the family the phase census flagged as
7
+ * hardest, because "when does this hold the channel" does not obviously map onto a visitor
8
+ * condition or a one-shot arrival the way an entrance or a gesture does — a scrub is driven by
9
+ * scroll position, which is continuous, not a boolean the way `:hover` or `[aria-expanded]` is.
10
+ * The question worth separating is *whether it changes* (yes, every frame) from *whether it ever
11
+ * hands the channel back* (no, never, for as long as the instance lives), and it is the second one
12
+ * `EffectPhase` actually asks.
13
+ *
14
+ * Every primitive in this file writes its claimed channel(s) **unconditionally from the moment it
15
+ * activates** — `preparePin`'s `installSticky` sets `position: sticky` inline the instant `prepare`
16
+ * runs, not the instant the element is actually in range; `prepareHorizontal`/`prepareMediaScrub`/
17
+ * `prepareProgress`/`prepareScrollSpy` all start writing on the very first scroll frame, which for a
18
+ * `load`-activated effect (their shared `defaultActivation`) is essentially immediately — and never
19
+ * releases it until `destroy()` tears the instance down. That is a structurally different shape from
20
+ * `state`: a `:hover`/drag/press effect writes *nothing at all* until the visitor's own action
21
+ * starts it, which is what lets an entrance's one-shot fill resolve first and hand the channel to a
22
+ * quiescent neighbour that has not spoken yet (`gestures/index.ts`'s `draggable` family is exactly
23
+ * this — silent until `pointerdown`). A scroll effect never has that silent stretch: it is live from
24
+ * the same instant an entrance would be trying to release into it, so treating them as taking turns
25
+ * (`state`'s exemption) would be describing a truce that doesn't exist. "Runs unbounded, so it never
26
+ * yields the channel at all" is `EffectPhase`'s own definition of `idle`, and `EffectInstance
27
+ * .continuous`'s doc (`core/types.ts`) already names this exact set — "a pin, a scroll progress
28
+ * track, a media scrub" — as the library's canonical unbounded, no-end effects. `idle` is that fact
29
+ * restated as a composition phase, not a new claim.
30
+ *
31
+ * `scroll-spy` earns the same label by the same test even though it writes an *attribute* rather
32
+ * than a style: `data-kui-active` moves between links continuously as sections cross the threshold,
33
+ * with no moment where nothing is marked active and the channel is free.
34
+ *
35
+ * `smooth-scroll-to` and `scroll-snap-x`/`-y` are not wrapped in `continuousSetup` — they set one
36
+ * declaration at `prepare` and are done, so the animator considers the *instance* finished
37
+ * immediately. That is orthogonal to the question here: the declaration they wrote
38
+ * (`scroll-behavior`, `scroll-snap-type`) stays applied inline for the instance's whole life exactly
39
+ * like the continuous ones, never conditionally, never released early. A permanently-held static
40
+ * write and a per-frame recomputed one both fail `state`'s test the same way.
41
+ *
42
+ * Declaring `idle` does not, in itself, open any new composition today — `INDEPENDENT_PHASES` in
43
+ * `core/channels.ts` only exempts `entrance|state` and `exit|state`, and `idle` pairs with nothing,
44
+ * itself included, which is the same outcome an undeclared phase already produced. What it changes
45
+ * is the meaning of that outcome: these thirteen collide with a channel-sharing neighbour because
46
+ * two effects are provably fighting over the same property at the same time (checked directly —
47
+ * `horizontal-scroll` and any `translate`-writing entrance both actively write `translate` from the
48
+ * moment the entrance would want to release, so there is no daylight to compose into), not because
49
+ * nothing was known about them. `test/catalog-phase-mechanics.test.ts` asserts both halves of that:
50
+ * the declaration is present, and a same-channel pair inside this family still correctly refuses.
51
+ *
52
+ * None of the eleven declares `transitions` — `phaseOf` (`core/compile.ts`) only ever *derives*
53
+ * `state` from that field, so a preset carrying both would be duplicate, driftable data, and every
54
+ * primitive here is `renderer: 'javascript'` with nothing rendered through a CSS `transition:` to
55
+ * begin with.
5
56
  */
6
57
  export declare const SCROLL_PRESETS: Preset[];