kuinetic 0.1.3 → 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 (125) 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 +15218 -5595
  13. package/dist/kuinetic.all.min.js +2 -0
  14. package/dist/kuinetic.css +2900 -360
  15. package/dist/kuinetic.js +15217 -5593
  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 +705 -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/background-media.d.ts +152 -0
  83. package/dist/types/effects/catalog/discrete.d.ts +13 -0
  84. package/dist/types/effects/catalog/interaction-proximity.d.ts +24 -0
  85. package/dist/types/effects/catalog/interaction-reveal.d.ts +248 -0
  86. package/dist/types/effects/catalog/interaction-shared.d.ts +42 -0
  87. package/dist/types/effects/catalog/interaction-states.d.ts +68 -0
  88. package/dist/types/effects/catalog/materials.d.ts +67 -0
  89. package/dist/types/effects/catalog/media-shared.d.ts +3 -2
  90. package/dist/types/effects/catalog/numbers-shared.d.ts +8 -5
  91. package/dist/types/effects/catalog/shared.d.ts +2 -2
  92. package/dist/types/effects/catalog/text-shared.d.ts +92 -10
  93. package/dist/types/effects/catalog/transforms.d.ts +13 -0
  94. package/dist/types/effects/catalog/view-transitions.d.ts +13 -0
  95. package/dist/types/effects/forms/primitives.d.ts +15 -4
  96. package/dist/types/effects/gestures/index.d.ts +53 -0
  97. package/dist/types/effects/index.d.ts +5 -0
  98. package/dist/types/effects/layout/presets.d.ts +35 -0
  99. package/dist/types/effects/motion-path/index.d.ts +35 -0
  100. package/dist/types/effects/scroll-mechanics/params.d.ts +23 -0
  101. package/dist/types/effects/scroll-mechanics/presets.d.ts +51 -0
  102. package/dist/types/effects/scroll-mechanics/section-index.d.ts +47 -0
  103. package/dist/types/effects/shared.d.ts +162 -2
  104. package/dist/types/effects/step-index.d.ts +121 -0
  105. package/dist/types/effects/step-marking.d.ts +41 -33
  106. package/dist/types/effects/svg/icon-parts.d.ts +23 -0
  107. package/dist/types/effects/three-d/flip-parts.d.ts +46 -0
  108. package/dist/types/effects/tween/index.d.ts +57 -0
  109. package/dist/types/effects/tween/properties.d.ts +107 -0
  110. package/dist/types/effects/tween/waypoints.d.ts +106 -0
  111. package/dist/types/showcase/compare.d.ts +4 -0
  112. package/dist/types/showcase/device-frame.d.ts +3 -0
  113. package/dist/types/showcase/hotspots.d.ts +3 -0
  114. package/dist/types/showcase/index.d.ts +30 -0
  115. package/dist/types/showcase/lightbox.d.ts +3 -0
  116. package/dist/types/showcase/media-source.d.ts +13 -0
  117. package/dist/types/showcase/modal-shell.d.ts +22 -0
  118. package/dist/types/showcase/scroll-story.d.ts +4 -0
  119. package/dist/types/showcase/shared.d.ts +47 -0
  120. package/dist/types/showcase/slideshow.d.ts +3 -0
  121. package/dist/types/showcase/slow-mo.d.ts +5 -0
  122. package/package.json +14 -7
  123. package/dist/esm/chunk-5DON3UFQ.mjs +0 -4380
  124. package/dist/esm/chunk-JT4PZL3A.mjs +0 -773
  125. package/dist/esm/chunk-QUVFODSQ.mjs +0 -1278
@@ -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[];
@@ -0,0 +1,47 @@
1
+ import type { PrepareContext } from '../../core/effect-context.js';
2
+ import type { ScrollFrame } from '../../core/scroll-scheduler.js';
3
+ import type { Cleanup } from '../../core/types.js';
4
+ /**
5
+ * Options for tracking the active section index as the scroller advances.
6
+ */
7
+ export interface SectionIndexOptions {
8
+ el: Element;
9
+ sections: Element[];
10
+ ctx: PrepareContext;
11
+ offsetTop?: string;
12
+ onChange: (next: number, previous: number) => void;
13
+ }
14
+ export type SectionIndex = Cleanup & {
15
+ release: () => void;
16
+ };
17
+ /**
18
+ * `offset-top` resolved to pixels for one frame.
19
+ *
20
+ * Evaluated statically per frame using the scheduler's viewport metrics so viewport units
21
+ * like `50vh` update with resize.
22
+ *
23
+ * @complexity O(n) time in the authored value's length; O(1) space.
24
+ * @overallScore 100
25
+ */
26
+ export declare function offsetTopPixels(authored: string, frame: ScrollFrame): number;
27
+ /**
28
+ * The highest index whose content-relative top has reached the reference line.
29
+ *
30
+ * `-1` when none has — before the first section, or with no sections at all. Callers rely on this
31
+ * being the *only* thing that decides "active": one number, or none, never more than one.
32
+ *
33
+ * @complexity O(n) time in section count; O(1) space.
34
+ * @overallScore 100
35
+ */
36
+ export declare function highestReachedIndex(tops: number[], scrollTop: number, line: number): number;
37
+ /**
38
+ * Measure section tops once per resize epoch and report the highest reached section index on change.
39
+ *
40
+ * Shared measuring and scroll subscription half extracted from `prepareScrollSpyContainer`,
41
+ * reused by `scroll-spy` and `scroll-story`.
42
+ *
43
+ * @complexity O(n) time and space in section count, recomputed every frame — cheap arithmetic
44
+ * against a rect cache, never a fresh layout read once per resize epoch has passed.
45
+ * @overallScore 100
46
+ */
47
+ export declare function createSectionIndex(options: SectionIndexOptions): SectionIndex;
@@ -1,8 +1,168 @@
1
- import type { Activation, ParameterSchema, Primitive, ReducedMotionPolicy, Timeline } from '../core/types.js';
1
+ import type { PrepareContext } from '../core/effect-context.js';
2
+ import type { Activation, EffectParams, NamedActivation, ParameterSchema, Primitive, ReducedMotionPolicy, Timeline } from '../core/types.js';
3
+ /**
4
+ * The one `delay` declaration, spread by every primitive that waits for a trigger before playing.
5
+ *
6
+ * Two spellings of an authored delay exist and they arrive by different routes. The positional
7
+ * one (`count-up 320ms 300ms`) rides alongside the parameter record as `params.timing` — see the
8
+ * design note in `core/js-effect-preparer.ts` for why timing is deliberately *not* merged into
9
+ * that record — while the named one (`count-up delay:300ms`) reaches a primitive only if its
10
+ * schema declares `delay`, and is otherwise dropped by `resolveParams` as an unknown parameter.
11
+ * So every primitive whose start moment is a trigger has to declare this, and declaring it is
12
+ * safe precisely because `0ms` is a true no-op: `readEffectParams` pre-fills every declared
13
+ * parameter, so a non-zero default would be indistinguishable from an authored value.
14
+ *
15
+ * Spread (`...TRIGGER_DELAY_PARAM`) rather than retyped so that rationale has one home.
16
+ *
17
+ * "Waits for a trigger" is wider than `defaultActivation: 'enter'`, which is all the structural
18
+ * guard in `test/js-effect-timing.test.ts` originally covered. A hover lift, an `aria-expanded`
19
+ * icon toggle and a FLIP reorder all default to `'load'` — they install listeners or an observer
20
+ * at load — yet each has a perfectly definite start moment later on (the pointer arrives, the
21
+ * attribute flips, the children move), and "start 200ms after that moment" is a coherent request
22
+ * for every one of them. `test/js-effect-timing-parity.test.ts` now guards both groups; its
23
+ * `TIMING_REFUSALS` table names the primitives that genuinely have no start moment and must *not*
24
+ * pick this up.
25
+ */
26
+ export declare const TRIGGER_DELAY_PARAM: ParameterSchema;
27
+ /** The three positional timing tokens of the grammar: `data-kui="fade-up 600ms 200ms linear"`. */
28
+ export type TimingToken = 'duration' | 'delay' | 'ease';
29
+ /** All three, in grammar order. Also the `honours` list of a primitive that supports the lot. */
30
+ export declare const ALL_TIMING_TOKENS: readonly TimingToken[];
31
+ /**
32
+ * What a JS-rendered primitive can actually *do* with authored timing.
33
+ *
34
+ * The problem this closes: the two spellings of a timing value fail differently, and one of them
35
+ * fails silently. `pin-section delay:300ms` reaches `readParams`, is not in the schema, and warns
36
+ * as an unknown parameter — the author is told. `pin-section 0ms 300ms` parses into
37
+ * `spec.delay`, is lifted out to `params.timing` (see `core/js-effect-preparer.ts`), and is then
38
+ * simply never read by a primitive that has no clock to shift. Nothing warns, nothing happens,
39
+ * and the page gives the author no way to find out which of those two it was.
40
+ *
41
+ * So a primitive that cannot honour a token has to say so, and it has to say so about the
42
+ * positional spelling too. Declaring the contract is how it does that.
43
+ */
44
+ export interface TimingContract {
45
+ /**
46
+ * Tokens this primitive genuinely acts on. Anything omitted warns by name when authored.
47
+ * Omit the field entirely for a primitive that honours none of the three.
48
+ */
49
+ honours?: readonly TimingToken[];
50
+ /**
51
+ * Completes `"<id>" cannot honour <token>: <because>`. Write the *reason*, not the symptom —
52
+ * "it tracks pointer position continuously, so there is no start moment to delay" tells an
53
+ * author to stop looking for a spelling that works, where "unsupported" does not.
54
+ */
55
+ because: string;
56
+ }
57
+ /**
58
+ * How long an effect waits after its trigger before it starts, in milliseconds.
59
+ *
60
+ * The named form of the pattern `text.ts` spells out at three call sites —
61
+ * `params.timing.delayMs ?? params.ms('delay', 0)` — and the sibling of
62
+ * `core/js-params.ts`'s `effectDurationMs`. Two spellings, one intent: `split-flap 400ms 200ms`
63
+ * and `split-flap 400ms delay:200ms` mean the same thing, and the positional one is what `play()`
64
+ * emits, so it is the one that must not be the one dropped.
65
+ *
66
+ * It lives here rather than beside `effectDurationMs` only because `src/core/` is being edited in
67
+ * parallel; the two belong together and should be moved once that settles.
68
+ *
69
+ * @param fallback - Used when the author wrote neither spelling. Effectively always `0`.
70
+ * @complexity O(1) time and space.
71
+ * @overallScore 100
72
+ */
73
+ export declare function effectDelayMs(params: EffectParams, fallback?: number): number;
74
+ /**
75
+ * The curve an effect runs on, preferring the positional token over the same-named parameter.
76
+ *
77
+ * The easing half of the same story. A primitive reading only `params.text('ease')` sees
78
+ * `flip-shuffle ease:linear` and not `flip-shuffle 400ms 0ms linear`, which is the identical
79
+ * request written the way the grammar puts it first.
80
+ *
81
+ * @param fallback - Used when the author wrote neither spelling.
82
+ * @complexity O(1) time and space.
83
+ * @overallScore 100
84
+ */
85
+ export declare function effectEasing(params: EffectParams, fallback?: string): string;
86
+ /**
87
+ * Copy the honoured positional tokens onto the primitive's own namespaced custom properties.
88
+ *
89
+ * Only the `key:value` spelling reaches CSS on its own: `compile.buildPlan` runs `resolveParams`
90
+ * over `spec.params` for every effect, but `pushTrack` — which is where `spec.duration`/`.delay`/
91
+ * `.easing` are turned into declarations — runs for `css-keyframes` primitives only. So a
92
+ * stylesheet-driven JS primitive sees `lift duration:400ms` and not `lift 400ms`, which is the
93
+ * same value written the other way round.
94
+ *
95
+ * Written through the ledger and therefore restored on teardown, and written *after*
96
+ * `applyStylePlan` has already put any `key:value` overrides inline — so the positional spelling
97
+ * wins a collision, matching `effectDurationMs`'s `params.timing.durationMs ?? params.ms(...)`
98
+ * precedence rather than contradicting it.
99
+ *
100
+ * Takes the whole context rather than the ledger off it so that an unauthored effect never
101
+ * dereferences `ctx` at all. That is not fastidiousness: `test/three-d.test.ts` calls the
102
+ * state-driven presets' `prepare` with no element and no context, asserting that the default path
103
+ * reaches for neither, and a caller like `prepareCardToggle` runs this before it knows whether it
104
+ * has anything else to do.
105
+ *
106
+ * @complexity O(1) time and space — three tokens, fixed.
107
+ * @overallScore 100
108
+ */
109
+ export declare function mirrorTimingToCss(id: string, honours: readonly TimingToken[], params: EffectParams, ctx: PrepareContext): void;
110
+ /**
111
+ * Wrap a primitive's own `prepare` so authored timing it cannot act on warns instead of vanishing.
112
+ *
113
+ * For primitives that drive their own frames. The wrapped `prepare` still does all the work; this
114
+ * only adds the diagnostics, which is why it composes with `deferPrepare` rather than replacing it.
115
+ *
116
+ * @param id - The primitive's id, named in the warning.
117
+ * @param contract - What this primitive honours, and why it cannot honour the rest.
118
+ * @param prepare - The primitive's real setup.
119
+ * @complexity O(1) time and space beyond the wrapped call.
120
+ * @overallScore 100
121
+ */
122
+ export declare function withTimingContract(id: string, contract: TimingContract, prepare: NonNullable<Primitive['prepare']>): NonNullable<Primitive['prepare']>;
123
+ /**
124
+ * The `prepare` for a primitive whose motion is a stylesheet transition or keyframe rather than
125
+ * frames this library drives — the hover family, the icon toggles, the flip card.
126
+ *
127
+ * These used to be `prepare: () => inertInstance()`, registered purely so the name parses and
128
+ * `data-kui-fx` gets stamped. That was almost right: the *named* spelling of a timing parameter
129
+ * already reaches their CSS through `resolveParams`, so there was nothing for JS to do. The
130
+ * positional spelling had no route at all, which is the gap this closes — plus the warning for
131
+ * whichever tokens the shipped rule pins (a linear spin's `ease`, say).
132
+ *
133
+ * @param id - The primitive's id; also the namespace of the properties written.
134
+ * @param contract - Which tokens the stylesheet actually reads, and why it ignores the others.
135
+ * @complexity O(1) time and space.
136
+ * @overallScore 100
137
+ */
138
+ export declare function stylesheetTimingPrepare(id: string, contract: TimingContract): NonNullable<Primitive['prepare']>;
139
+ /**
140
+ * "Accepts every timeline, is driven by none of them."
141
+ *
142
+ * A few primitives have no relationship to progress at all: `background-media` paints a backdrop,
143
+ * and the scroll-mechanics drivers read the scroll position themselves rather than being driven by
144
+ * an `animation-timeline`. None of them ever reads `Timeline`.
145
+ *
146
+ * They still have to *accept* one, because `compile.ts`'s `intersect` narrows a composed element's
147
+ * timeline support to what every primitive on it supports. Declaring the honest answer — `['time']`,
148
+ * or nothing — empties that intersection for `data-kui="background-media src:/hero.mp4, parallax"`
149
+ * with `timeline:view`, `style-plan.ts` then refuses the timeline, and the neighbour's scrub
150
+ * silently degrades to a one-shot. Nothing is wrong with either effect; the intersection is simply
151
+ * the wrong question to ask of a primitive that has no opinion.
152
+ *
153
+ * So the list stays exhaustive and this name is what makes it truthful. Written out inline it reads
154
+ * as a claim to support all four — the same lie in four places, each needing its own paragraph to
155
+ * walk back. Written as `supportedTimelines: TIMELINE_AGNOSTIC` it reads as what it is: an
156
+ * abstention, not a claim.
157
+ *
158
+ * One shared array instance, like `ENTRANCE_TIMELINES` in `catalog/core.ts`. Every consumer
159
+ * (`intersect`, `warnUnsupportedTimeline`) only reads it.
160
+ */
161
+ export declare const TIMELINE_AGNOSTIC: Timeline[];
2
162
  export interface CssPrimitiveOptions {
3
163
  parameters?: ParameterSchema;
4
164
  timelines?: Timeline[];
5
- activations?: Activation[];
165
+ activations?: NamedActivation[];
6
166
  defaultActivation?: Activation;
7
167
  reducedMotion?: ReducedMotionPolicy;
8
168
  perfClass?: Primitive['perfClass'];
@@ -0,0 +1,121 @@
1
+ import type { Cleanup, EffectParams } from '../core/types.js';
2
+ import type { PrepareContext } from '../core/effect-context.js';
3
+ import type { TargetScope } from '../core/target.js';
4
+ export interface StepIndexOptions {
5
+ el: Element;
6
+ params: EffectParams;
7
+ ctx: PrepareContext;
8
+ scope: TargetScope;
9
+ resolveSteps: () => Iterable<Element>;
10
+ onRender?: (index: number, total: number) => void;
11
+ clickFallback?: boolean;
12
+ name?: string;
13
+ }
14
+ export interface StepIndex {
15
+ goTo(index: number): void;
16
+ next(): void;
17
+ prev(): void;
18
+ current(): number;
19
+ total(): number;
20
+ release(): void;
21
+ }
22
+ /**
23
+ * Advance a step index, wrapping back to 0 — pure, so the wrap rule is assertable without a DOM.
24
+ *
25
+ * @complexity O(1) time and space.
26
+ * @overallScore 100
27
+ */
28
+ export declare function nextStep(step: number, total: number): number;
29
+ /**
30
+ * Step backwards, wrapping to the last index — the mirror of `nextStep`, and pure for the same
31
+ * reason: the wrap is the whole of the rule and it is worth asserting without a DOM.
32
+ *
33
+ * `+ total` before the modulo rather than a `< 0` branch: `-1 % 5` is `-1` in JS, not `4`, so the
34
+ * naive mirror of `nextStep` silently produces a negative index that marks nothing and reads as a
35
+ * dead control rather than an error.
36
+ *
37
+ * @complexity O(1) time and space.
38
+ * @overallScore 100
39
+ */
40
+ export declare function prevStep(step: number, total: number): number;
41
+ /**
42
+ * Clamp an authored or computed index into range, wrapping the same way the steppers do.
43
+ *
44
+ * `jump:` takes its index from a control's position among its siblings, which is trustworthy —
45
+ * but `steps:` is authored separately, so a page with six dots and `steps:4` would otherwise set
46
+ * an index no step element has. Wrapping rather than clamping keeps one rule across all three
47
+ * controls instead of two.
48
+ *
49
+ * @complexity O(1) time and space.
50
+ * @overallScore 100
51
+ */
52
+ export declare function clampStep(step: number, total: number): number;
53
+ /**
54
+ * How many steps there are, asked freshly on each call rather than fixed at setup.
55
+ *
56
+ * `steps:` used to be required and defaulted to 4, which is the wrong shape for a deck: the number
57
+ * is not a *choice* an author makes, it is a fact about how many slides they wrote, and stating it
58
+ * twice means a sixth slide that silently never gets reached. Counting instead makes `steps:` an
59
+ * override for the case the elements cannot answer — and asking per flip rather than once means a
60
+ * slide added after setup is picked up, matching `createStepMarker`, which already re-resolves per
61
+ * flip for exactly that reason.
62
+ *
63
+ * Counted per *parent*, taking the largest group, not as a flat total. `target:` routinely names
64
+ * two parallel groups — the slides and the dots that track them — and a flat count of a five-slide
65
+ * deck with five dots is ten. Per-parent is also precisely how the marker numbers them, so the
66
+ * count and the marking cannot disagree.
67
+ *
68
+ * Exported for `effects/carousel`, which drives an index over the same shape of markup and must
69
+ * count it the same way. A second implementation of "how many slides are there" is exactly the
70
+ * drift `createStepMarker` was extracted to end for the marking half.
71
+ *
72
+ * @complexity O(n) time and space in the matched elements.
73
+ * @overallScore 100
74
+ */
75
+ export declare function countSteps(params: EffectParams, resolveSteps: () => Iterable<Element>): number;
76
+ /** One named control group: the selector the author wrote, and what pressing a match does. */
77
+ export interface ControlGroup {
78
+ selector: string;
79
+ /** @param position - The pressed control's place among its own group, in document order. */
80
+ run: (node: Element, position: number) => void;
81
+ }
82
+ /**
83
+ * Bind every named control group with a *single* delegated listener on the scope root.
84
+ *
85
+ * One listener per matched node was the obvious form and it had a hole with no warning attached:
86
+ * the nodes are matched once, at prepare time, so a dot rendered afterwards was inert forever. The
87
+ * arrows kept working — they ask `countSteps` for the total on every press, so they reached the new
88
+ * slide fine — which made the failure look like "the dots are broken" rather than "controls are
89
+ * bound once". Delegation removes the setup-time snapshot entirely: a control is whatever matches
90
+ * the selector at the moment of the press, which is the same rule `resolveSteps` and `countSteps`
91
+ * already follow, and it collapses teardown from N removals to one.
92
+ *
93
+ * The root is the *scope* root, not simply the host. Under the page scope `step-progress` has
94
+ * always resolved with, a control may legitimately sit outside the element it drives — arrows in a
95
+ * section header above the deck — and a listener on the host would never see those clicks.
96
+ *
97
+ * Exported for `effects/carousel` alongside `countSteps`: `next:`/`prev:`/`jump:` mean the same
98
+ * thing on a ring as they do on a bar, and every subtlety in the walk below (the `:scope` root, the
99
+ * per-press re-query, the `scope:self` containment) was found once and should not be found again.
100
+ *
101
+ * @returns The teardown for the one listener.
102
+ * @complexity O(g × (m + d)) per click — one scoped query and one ancestor walk per named group,
103
+ * in that group's matches `m` and the pressed node's depth `d`; O(m) space for the largest group.
104
+ * @overallScore 100
105
+ */
106
+ export declare function delegateControls(request: {
107
+ el: Element;
108
+ ctx: PrepareContext;
109
+ scope: TargetScope;
110
+ groups: ControlGroup[];
111
+ }): Cleanup;
112
+ /**
113
+ * Shared step index for click-driven steppers and showcase slideshows.
114
+ *
115
+ * Re-queries steps on every render so added or removed slides cannot leave stale marks. The
116
+ * optional render hook lets a widget mirror the selected index into ARIA without owning it twice.
117
+ *
118
+ * @complexity O(n) per render in the step and control groups; O(n) marker storage.
119
+ * @overallScore 100
120
+ */
121
+ export declare function createStepIndex(options: StepIndexOptions): StepIndex;