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.
- package/README.md +93 -10
- package/dist/esm/advanced/index.mjs +4727 -0
- package/dist/esm/chunk-AEQGC2KM.mjs +189 -0
- package/dist/esm/chunk-MQSNMYRP.mjs +9332 -0
- package/dist/esm/chunk-RVTYN6NZ.mjs +3446 -0
- package/dist/esm/chunk-U43BX5O2.mjs +2984 -0
- package/dist/esm/core/index.mjs +16 -9
- package/dist/esm/effects/index.mjs +19 -4
- package/dist/esm/index.mjs +17 -10
- package/dist/kuinetic.advanced.js +5070 -0
- package/dist/kuinetic.advanced.min.js +749 -0
- package/dist/kuinetic.all.js +15147 -6039
- package/dist/kuinetic.all.min.js +2 -0
- package/dist/kuinetic.css +2801 -318
- package/dist/kuinetic.js +15147 -6038
- package/dist/kuinetic.min.css +1 -0
- package/dist/kuinetic.min.js +7 -0
- package/dist/types/3d/angles.d.ts +30 -0
- package/dist/types/3d/flattening.d.ts +49 -0
- package/dist/types/3d/index.d.ts +36 -0
- package/dist/types/3d/model-3d.d.ts +115 -0
- package/dist/types/3d/register.d.ts +26 -0
- package/dist/types/3d/webgl.d.ts +24 -0
- package/dist/types/advanced/audio.d.ts +322 -0
- package/dist/types/advanced/base.d.ts +233 -0
- package/dist/types/advanced/camera-3d.d.ts +236 -0
- package/dist/types/advanced/fluid-cursor.d.ts +149 -0
- package/dist/types/advanced/gl-utils.d.ts +52 -0
- package/dist/types/advanced/glsl.d.ts +60 -0
- package/dist/types/advanced/index.d.ts +23 -0
- package/dist/types/advanced/particles.d.ts +108 -0
- package/dist/types/advanced/scenes.d.ts +207 -0
- package/dist/types/advanced/shaders.d.ts +972 -0
- package/dist/types/browser/boot.d.ts +72 -0
- package/dist/types/core/activation.d.ts +227 -2
- package/dist/types/core/animator.d.ts +446 -11
- package/dist/types/core/attrs.d.ts +9 -0
- package/dist/types/core/breakpoints.d.ts +260 -0
- package/dist/types/core/bundles.d.ts +71 -0
- package/dist/types/core/callback.d.ts +64 -0
- package/dist/types/core/capabilities.d.ts +47 -0
- package/dist/types/core/channels.d.ts +120 -2
- package/dist/types/core/cloak-selectors.d.ts +47 -0
- package/dist/types/core/compile.d.ts +207 -2
- package/dist/types/core/control.d.ts +218 -0
- package/dist/types/core/declarations.d.ts +128 -0
- package/dist/types/core/derived/aggregate.d.ts +43 -0
- package/dist/types/core/derived/book.d.ts +30 -0
- package/dist/types/core/derived/diagnostics.d.ts +51 -0
- package/dist/types/core/derived/group-gate.d.ts +62 -0
- package/dist/types/core/derived/install.d.ts +110 -0
- package/dist/types/core/derived/late-matches.d.ts +19 -0
- package/dist/types/core/derived/types.d.ts +108 -0
- package/dist/types/core/easing.d.ts +57 -0
- package/dist/types/core/element-config.d.ts +47 -6
- package/dist/types/core/element-size.d.ts +23 -0
- package/dist/types/core/event-sources.d.ts +39 -0
- package/dist/types/core/flip.d.ts +34 -1
- package/dist/types/core/gesture.d.ts +12 -0
- package/dist/types/core/host-facts.d.ts +155 -0
- package/dist/types/core/index.d.ts +7 -3
- package/dist/types/core/owned-styles.d.ts +129 -2
- package/dist/types/core/params.d.ts +48 -0
- package/dist/types/core/parse.d.ts +28 -1
- package/dist/types/core/register-into.d.ts +29 -0
- package/dist/types/core/registry.d.ts +10 -3
- package/dist/types/core/repeat.d.ts +91 -0
- package/dist/types/core/sequence.d.ts +126 -0
- package/dist/types/core/stagger-config.d.ts +199 -0
- package/dist/types/core/stagger-keys.d.ts +63 -0
- package/dist/types/core/stagger.d.ts +187 -6
- package/dist/types/core/style-plan.d.ts +21 -16
- package/dist/types/core/target.d.ts +128 -0
- package/dist/types/core/threshold-reachability.d.ts +45 -0
- package/dist/types/core/time-scale.d.ts +9 -0
- package/dist/types/core/toggle-actions.d.ts +141 -0
- package/dist/types/core/travel.d.ts +59 -0
- package/dist/types/core/types.d.ts +699 -13
- package/dist/types/core/unquoted-selectors.d.ts +53 -0
- package/dist/types/effects/carousel/drag.d.ts +80 -0
- package/dist/types/effects/carousel/index.d.ts +149 -0
- package/dist/types/effects/catalog/discrete.d.ts +13 -0
- package/dist/types/effects/catalog/interaction-proximity.d.ts +24 -0
- package/dist/types/effects/catalog/interaction-reveal.d.ts +248 -0
- package/dist/types/effects/catalog/interaction-shared.d.ts +42 -0
- package/dist/types/effects/catalog/interaction-states.d.ts +68 -0
- package/dist/types/effects/catalog/materials.d.ts +67 -0
- package/dist/types/effects/catalog/media-shared.d.ts +3 -2
- package/dist/types/effects/catalog/numbers-shared.d.ts +8 -5
- package/dist/types/effects/catalog/shared.d.ts +1 -1
- package/dist/types/effects/catalog/text-shared.d.ts +86 -10
- package/dist/types/effects/catalog/transforms.d.ts +13 -0
- package/dist/types/effects/catalog/view-transitions.d.ts +13 -0
- package/dist/types/effects/forms/primitives.d.ts +15 -4
- package/dist/types/effects/gestures/index.d.ts +53 -0
- package/dist/types/effects/index.d.ts +5 -0
- package/dist/types/effects/layout/presets.d.ts +35 -0
- package/dist/types/effects/motion-path/index.d.ts +35 -0
- package/dist/types/effects/scroll-mechanics/params.d.ts +23 -0
- package/dist/types/effects/scroll-mechanics/presets.d.ts +51 -0
- package/dist/types/effects/scroll-mechanics/section-index.d.ts +47 -0
- package/dist/types/effects/shared.d.ts +124 -4
- package/dist/types/effects/step-index.d.ts +121 -0
- package/dist/types/effects/step-marking.d.ts +41 -33
- package/dist/types/effects/svg/icon-parts.d.ts +23 -0
- package/dist/types/effects/three-d/flip-parts.d.ts +46 -0
- package/dist/types/effects/tween/index.d.ts +57 -0
- package/dist/types/effects/tween/properties.d.ts +107 -0
- package/dist/types/effects/tween/waypoints.d.ts +106 -0
- package/dist/types/showcase/compare.d.ts +4 -0
- package/dist/types/showcase/device-frame.d.ts +3 -0
- package/dist/types/showcase/hotspots.d.ts +3 -0
- package/dist/types/showcase/index.d.ts +30 -0
- package/dist/types/showcase/lightbox.d.ts +3 -0
- package/dist/types/showcase/media-source.d.ts +13 -0
- package/dist/types/showcase/modal-shell.d.ts +22 -0
- package/dist/types/showcase/scroll-story.d.ts +4 -0
- package/dist/types/showcase/shared.d.ts +47 -0
- package/dist/types/showcase/slideshow.d.ts +3 -0
- package/dist/types/showcase/slow-mo.d.ts +5 -0
- package/package.json +14 -7
- package/dist/esm/chunk-7WMNPIOZ.mjs +0 -4883
- package/dist/esm/chunk-JT4PZL3A.mjs +0 -773
- package/dist/esm/chunk-TJDDIQRG.mjs +0 -1290
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { EffectSpec } from './types.js';
|
|
2
|
+
import type { Registry } from './registry.js';
|
|
3
|
+
/**
|
|
4
|
+
* Owned by phase 9b — catching the classic `target:` typo: `pop target:.a, .b` reads as three
|
|
5
|
+
* effect segments (`pop`, `.a`, `.b`), not one effect with a two-part selector, because a comma
|
|
6
|
+
* separates *segments* everywhere else in the grammar and `target:` is the one place a selector
|
|
7
|
+
* can legitimately contain one. An author who forgets the quotes (`target:'.a, .b'`) gets a stray
|
|
8
|
+
* segment named `.a` that is not a registered effect and looks exactly like a selector — this
|
|
9
|
+
* module finds that shape and 9b's diagnostics surface it via `data-kui-unmatched`.
|
|
10
|
+
*/
|
|
11
|
+
/** One stray segment that looks like a continuation of the previous segment's `target:` selector. */
|
|
12
|
+
export interface UnquotedSelector {
|
|
13
|
+
/** The previous segment's `target:` value, as authored. */
|
|
14
|
+
previous: string;
|
|
15
|
+
/** The stray segment's name — the text between commas that isn't a registered effect. */
|
|
16
|
+
segment: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Find segments whose first token is not a registered effect name, that look like a selector
|
|
20
|
+
* fragment (start with `.`, `#`, `[`, `*`, `:`, or contain `>`, `~`, `+`), and whose *previous*
|
|
21
|
+
* segment carries a `target:` token — the shape an unquoted, comma-containing selector leaves
|
|
22
|
+
* behind once `parse.ts` has already split it on the comma.
|
|
23
|
+
*
|
|
24
|
+
* Pure: it only reads `specs` and `registry`, and never changes how anything is parsed or
|
|
25
|
+
* compiled — the fix is a warning, not a silent re-join, because re-joining would have to guess
|
|
26
|
+
* where the selector was supposed to end.
|
|
27
|
+
*
|
|
28
|
+
* Known limitation: only catches a stray segment whose *first token* looks like a selector.
|
|
29
|
+
* `pop target:.a, li > a` splits into a stray segment `li > a` whose first token is `li` — a
|
|
30
|
+
* plausible effect name — so it is not flagged, even though `>` later in the segment is still a
|
|
31
|
+
* combinator. Detecting that shape would mean scanning every segment for a `target:`-less
|
|
32
|
+
* combinator, not just the one right after a `target:`-bearing segment, which is out of scope
|
|
33
|
+
* here: this pass only looks at the segment immediately following one that carries `target:`.
|
|
34
|
+
*
|
|
35
|
+
* @param specs - The document's parsed effect segments, in authored order. Pre-lift: `target:`
|
|
36
|
+
* still lives in `spec.params.target` rather than having been pulled onto a separate field.
|
|
37
|
+
* @param registry - Where a segment's first token is looked up to decide if it names a real
|
|
38
|
+
* effect.
|
|
39
|
+
* @returns Every suspect segment found, in authored order.
|
|
40
|
+
* @complexity O(n) time in `specs.length`, each iteration doing an O(1) registry lookup; O(f)
|
|
41
|
+
* space in the number of findings.
|
|
42
|
+
* @overallScore 100
|
|
43
|
+
*/
|
|
44
|
+
export declare function findUnquotedSelectors(specs: EffectSpec[], registry: Registry): UnquotedSelector[];
|
|
45
|
+
/**
|
|
46
|
+
* Render one {@link UnquotedSelector} finding as a warning string, naming both halves of the fix:
|
|
47
|
+
* quote the previous segment's selector so the whole comma list is one `target:` value.
|
|
48
|
+
*
|
|
49
|
+
* @param found - One finding from {@link findUnquotedSelectors}.
|
|
50
|
+
* @complexity O(1) time and space.
|
|
51
|
+
* @overallScore 100
|
|
52
|
+
*/
|
|
53
|
+
export declare function unquotedSelectorWarning(found: UnquotedSelector): string;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { PrepareContext } from '../../core/effect-context.js';
|
|
2
|
+
import type { Cleanup } from '../../core/types.js';
|
|
3
|
+
export interface RingDragRequest {
|
|
4
|
+
el: Element;
|
|
5
|
+
ctx: PrepareContext;
|
|
6
|
+
/** `grab:false` still installs the keyboard half — that is accessibility, not decoration. */
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
/** Pointer pixels per place on the ring. */
|
|
9
|
+
travelPx: number;
|
|
10
|
+
/** How many places the ring has *now*; asked per gesture, never captured. */
|
|
11
|
+
total(): number;
|
|
12
|
+
/** The ring's current continuous position, in places. */
|
|
13
|
+
positionOf(): number;
|
|
14
|
+
/** Move the ring to a continuous position and re-render. */
|
|
15
|
+
moveTo(position: number): void;
|
|
16
|
+
/** Publish or clear the dragging state the stylesheet suspends its transition on. */
|
|
17
|
+
setDragging(dragging: boolean): void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Where a release coasts to, before snapping.
|
|
21
|
+
*
|
|
22
|
+
* Velocity is in pixels per second and the ring is measured in places, so the projection is
|
|
23
|
+
* `velocity / travel * seconds` — one conversion, in one place, in the unit the index uses.
|
|
24
|
+
*
|
|
25
|
+
* Capped, and the cap is not politeness. Uncapped, a hard flick on a phone (velocities of several
|
|
26
|
+
* thousand pixels per second are ordinary) projects a dozen places, and a ring that spins a dozen
|
|
27
|
+
* cards from one flick has stopped being a control and become a slot machine: the visitor cannot
|
|
28
|
+
* predict where it lands, so they cannot aim, so they flick again. Four places is far enough that a
|
|
29
|
+
* deliberate throw clearly differs from a nudge and close enough that the destination stays
|
|
30
|
+
* legible.
|
|
31
|
+
*
|
|
32
|
+
* Pure and exported so both halves — the conversion and the cap — are assertable without a pointer.
|
|
33
|
+
*
|
|
34
|
+
* @param position - Where the ring is at release, in places.
|
|
35
|
+
* @param velocityPxPerSec - Signed pointer velocity along the drag axis.
|
|
36
|
+
* @param travelPx - Pixels per place.
|
|
37
|
+
* @returns The un-snapped position the ring is heading for.
|
|
38
|
+
* @complexity O(1) time and space.
|
|
39
|
+
*/
|
|
40
|
+
export declare function projectRelease(position: number, velocityPxPerSec: number, travelPx: number): number;
|
|
41
|
+
/**
|
|
42
|
+
* Which place a released ring settles on.
|
|
43
|
+
*
|
|
44
|
+
* Separate from {@link projectRelease} because they answer different questions and only one of them
|
|
45
|
+
* has an opinion: projection is physics, snapping is the contract that a ring always comes to rest
|
|
46
|
+
* facing a card. Splitting them is also what makes "a flick of exactly half a place still commits"
|
|
47
|
+
* testable without synthesising a gesture.
|
|
48
|
+
*
|
|
49
|
+
* `+ 0` is not decoration. `Math.round(-0.4)` is `-0`, and a ring settled at "minus place zero" is
|
|
50
|
+
* not a place — it is the same place spelled in a way that half of JavaScript disagrees with
|
|
51
|
+
* itself about. `-0 === 0` is true, so most arithmetic hides it; `Object.is(-0, 0)` is false, which
|
|
52
|
+
* is what `toBe` compares with, and `[-0].toString()` and `JSON.stringify(-0)` disagree with each
|
|
53
|
+
* other too. Adding zero normalises the sign at the one place a negative zero can be produced,
|
|
54
|
+
* rather than leaving every caller and every serialisation to find out for itself.
|
|
55
|
+
*
|
|
56
|
+
* @param projected - The coasting destination, in places.
|
|
57
|
+
* @returns The nearest whole place, never negative zero.
|
|
58
|
+
* @complexity O(1) time and space.
|
|
59
|
+
*/
|
|
60
|
+
export declare function snapTo(projected: number): number;
|
|
61
|
+
/**
|
|
62
|
+
* Bring a settled position onto `[0, total)`.
|
|
63
|
+
*
|
|
64
|
+
* A ring has no ends, so nothing about the geometry needs this — `--kui-offset` is computed modulo
|
|
65
|
+
* the count and `render` rebases the published position on the live step every time. What needs it
|
|
66
|
+
* is arithmetic hygiene over a long session: `position` accumulates every drag ever made, and a
|
|
67
|
+
* number in the millions loses the fractional precision the sub-step rotation is carried in. Doing
|
|
68
|
+
* it at rest rather than mid-drag keeps the drag itself monotonic, so a gesture never jumps because
|
|
69
|
+
* it happened to cross the seam.
|
|
70
|
+
*
|
|
71
|
+
* @complexity O(1) time and space.
|
|
72
|
+
*/
|
|
73
|
+
export declare function wrapPlace(place: number, total: number): number;
|
|
74
|
+
/**
|
|
75
|
+
* Install the grab and the keyboard on a ring.
|
|
76
|
+
*
|
|
77
|
+
* @returns Teardown for every listener and attribute this installs.
|
|
78
|
+
* @complexity O(1) per pointer event and per key; O(1) space.
|
|
79
|
+
*/
|
|
80
|
+
export declare function createRingDrag(request: RingDragRequest): Cleanup;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type { Preset, Primitive } from '../../core/types.js';
|
|
2
|
+
import type { Registry } from '../../core/registry.js';
|
|
3
|
+
/**
|
|
4
|
+
* Whether a slot at this ring angle has its own plane turned away from the camera.
|
|
5
|
+
*
|
|
6
|
+
* Pure and exported so the rule is assertable without a DOM or a layout. The angle is the slot's
|
|
7
|
+
* signed place on the ring in degrees, already including the live-step drift.
|
|
8
|
+
*
|
|
9
|
+
* `>=` rather than `>` at exactly a quarter turn: a slot edge-on to the camera has zero projected
|
|
10
|
+
* width, so it can only ever swallow a click that was aimed at something else. Calling it "back" is
|
|
11
|
+
* the answer that costs nothing and removes the boundary case from the hit-testing question.
|
|
12
|
+
*
|
|
13
|
+
* @param angleDeg - Signed degrees from the camera-facing position.
|
|
14
|
+
* @returns `'back'` when the slot's face points away, `'front'` otherwise.
|
|
15
|
+
* @complexity O(1) time and space.
|
|
16
|
+
*/
|
|
17
|
+
export declare function faceAt(angleDeg: number): 'front' | 'back';
|
|
18
|
+
/**
|
|
19
|
+
* Bring an angle onto `(-180, 180]`, the range where "how far from facing me" is a magnitude.
|
|
20
|
+
*
|
|
21
|
+
* Without it a slot at `+350deg` — which is 10 degrees from front — reads as further from the
|
|
22
|
+
* camera than one at `+100deg`, and the whole back half of a large ring is mislabelled.
|
|
23
|
+
*
|
|
24
|
+
* @complexity O(1) time and space.
|
|
25
|
+
*/
|
|
26
|
+
export declare function normaliseDegrees(angleDeg: number): number;
|
|
27
|
+
/**
|
|
28
|
+
* Where the ring's continuous position rounds to, and how far past it the ring currently sits.
|
|
29
|
+
*
|
|
30
|
+
* The pair the stylesheet needs and the reason both tokens are published rather than one: the
|
|
31
|
+
* integer decides which slot is *live* (and therefore what `--kui-offset` means for every other
|
|
32
|
+
* slot), and the remainder is the sub-step rotation on top of it. Re-snapping the integer whenever
|
|
33
|
+
* the remainder passes a half step is what keeps the remainder inside ±0.5, so the ring never has
|
|
34
|
+
* to travel back across the whole strip to wrap from the last slide to the first — the same
|
|
35
|
+
* property `circularOffset` gives the discrete case.
|
|
36
|
+
*
|
|
37
|
+
* @param position - Continuous ring position, in places. May be negative or beyond the count.
|
|
38
|
+
* @param total - How many places the ring has.
|
|
39
|
+
* @returns The live integer step, wrapped into range, and the signed remainder in `[-0.5, 0.5)`.
|
|
40
|
+
* @complexity O(1) time and space.
|
|
41
|
+
*/
|
|
42
|
+
export declare function snapPosition(position: number, total: number): {
|
|
43
|
+
step: number;
|
|
44
|
+
drift: number;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* An authored `<angle>` as a number of degrees.
|
|
48
|
+
*
|
|
49
|
+
* `params.ts` accepts every CSS angle unit and normalises only the bare/`d` spellings to `deg`, so
|
|
50
|
+
* a value reaching here can legitimately be `0.05turn` or `1.2rad`. The stylesheet does not care —
|
|
51
|
+
* it hands the token straight to `rotateY()` — but this file computes with it (to decide which
|
|
52
|
+
* slots face away), and a `Number('0.05turn')` is `NaN`.
|
|
53
|
+
*
|
|
54
|
+
* Falls back rather than throwing on an unrecognised unit for the reason every reader in this
|
|
55
|
+
* library does: the value has already been validated, so an unfamiliar spelling here means this
|
|
56
|
+
* function is behind `params.ts`, and a ring that spreads over the default arc is a better answer
|
|
57
|
+
* than one that throws during `prepare`.
|
|
58
|
+
*
|
|
59
|
+
* @param value - A validated CSS angle.
|
|
60
|
+
* @param fallbackDeg - Used when the unit is not one this function converts.
|
|
61
|
+
* @returns Degrees.
|
|
62
|
+
* @complexity O(n) time in value length; O(1) space.
|
|
63
|
+
*/
|
|
64
|
+
export declare function degreesOf(value: string, fallbackDeg: number): number;
|
|
65
|
+
/**
|
|
66
|
+
* The ring primitive.
|
|
67
|
+
*
|
|
68
|
+
* `renderer: 'javascript'` and two channels together are the honest declaration: this file paints
|
|
69
|
+
* nothing itself — it publishes numbers — but `carousel.css` does, and both of the things it writes
|
|
70
|
+
* have to be declared or the conflict detector waves through a composition that silently loses one
|
|
71
|
+
* effect's output.
|
|
72
|
+
*
|
|
73
|
+
* `skew` is this catalog's name for "claims the whole `transform` shorthand" (see `CHANNEL.skew` in
|
|
74
|
+
* `core/types.ts`), which `carousel.css` writes on the elements this effect drives. The shorthand is
|
|
75
|
+
* unavoidable here: three-dimensional placement needs `rotateX`/`rotateY`/`translateZ` composed in a
|
|
76
|
+
* fixed order, and `rotate:`/`translate:` as individual properties are applied in the order the spec
|
|
77
|
+
* fixes, which is the reverse of the one this geometry needs.
|
|
78
|
+
*
|
|
79
|
+
* `discrete` is `display`, and it is here for exactly the reason `card-toggle` declares it
|
|
80
|
+
* (`effects/three-d/index.ts`): the unconditional host rule pins `display: grid` so every slot
|
|
81
|
+
* starts from the same cell instead of flowing down the page as block siblings. That has nothing to
|
|
82
|
+
* do with `catalog/discrete.ts`'s show/hide use of the same property, but `display` is tracked as
|
|
83
|
+
* one channel whatever value is written into it — the point of the channel model is the physical
|
|
84
|
+
* property, not the intent — so a ring composed with an `@starting-style` open/close effect is now
|
|
85
|
+
* correctly reported as a collision rather than quietly letting one of the two win.
|
|
86
|
+
*
|
|
87
|
+
* `reducedMotion: 'shorten'`, not `'disable'`, for exactly the reason `step-progress` carries the
|
|
88
|
+
* same note: under `disable` the animator never activates the instance, so no index is published,
|
|
89
|
+
* no slot is marked, and the arrows do nothing. A visitor who asked for less motion would get a
|
|
90
|
+
* dead widget rather than a calm one. Reduced motion suppresses the *travel* between places — which
|
|
91
|
+
* `base.css`'s policy layer already does to the transition `carousel.css` declares — never the
|
|
92
|
+
* function.
|
|
93
|
+
*/
|
|
94
|
+
export declare const SPATIAL_RING_PRIMITIVE: Primitive;
|
|
95
|
+
export declare const CAROUSEL_PRIMITIVES: Primitive[];
|
|
96
|
+
/**
|
|
97
|
+
* Convex and concave are two names, not one parameter with two values.
|
|
98
|
+
*
|
|
99
|
+
* They are one sign in the maths — `translateZ(+r)` puts the camera outside the ring,
|
|
100
|
+
* `translateZ(-r)` puts it at the centre — and everything else about them differs. Outside, the far
|
|
101
|
+
* half of the ring is content: smaller, dimmer, and part of the composition, and a full 360-degree
|
|
102
|
+
* spread is right. Inside, the far half is *behind your head*, so a full spread hides most of the
|
|
103
|
+
* deck and the sensible default is a slice; the cards nearest the edges of that slice are the ones
|
|
104
|
+
* that need culling; and "next" moves the world past a fixed viewer rather than turning an object
|
|
105
|
+
* in front of one.
|
|
106
|
+
*
|
|
107
|
+
* A `curve:` keyword would have made all of that one name's problem to explain, with half its
|
|
108
|
+
* documentation prefixed by "unless". Two names each get their own defaults, their own rules in
|
|
109
|
+
* `carousel.css`, and their own paragraph.
|
|
110
|
+
*
|
|
111
|
+
* `requiresOwnSubtree` on all of them: every rule in `carousel.css` past the host rule reaches the
|
|
112
|
+
* slots, so a `target:` that relocated `data-kui-fx` onto one slot would leave those selectors
|
|
113
|
+
* looking for grandchildren nobody authored. (The primitive declares a `target` parameter of its
|
|
114
|
+
* own, so `compile.ts` never consults the flag — but `test/css-requires-own-subtree.test.ts`
|
|
115
|
+
* re-derives the reaching set from the stylesheet and does, and the fact it records is true.)
|
|
116
|
+
*
|
|
117
|
+
* `phase: 'idle'` on all of them too, and for the same reason `target` does not change the answer:
|
|
118
|
+
* `channelsFor`/`findConflicts` (`core/channels.ts`) reason about the channels *this preset
|
|
119
|
+
* declares* on the host it is authored on (`CHANNEL.skew` and `'discrete'`, from
|
|
120
|
+
* `SPATIAL_RING_PRIMITIVE`), not about which element in the subtree physically paints them — the
|
|
121
|
+
* compiler has no notion of "the primitive's own target moved the real work three nodes down," and
|
|
122
|
+
* does not need one here, because `requiresOwnSubtree` already keeps this preset off of anything
|
|
123
|
+
* `target:` could relocate.
|
|
124
|
+
*
|
|
125
|
+
* The phase question itself is the same one `scroll-mechanics/presets.ts` answers at length:
|
|
126
|
+
* `prepareSpatialRing` calls `render()` unconditionally during `prepare` — before any drag, before
|
|
127
|
+
* any control click — which stamps `data-kui-step`/`--kui-step-position`/the per-slot face
|
|
128
|
+
* attributes immediately, and the primitive's `defaultActivation` is `'load'`. So, like the scroll
|
|
129
|
+
* primitives and unlike `gestures/index.ts`'s `draggable` (silent until `pointerdown`), a ring never
|
|
130
|
+
* has a quiescent stretch for an entrance to release into — it claims `skew`/`discrete` from the
|
|
131
|
+
* first frame and holds them, drag or no drag, until `destroy()`. That is `EffectPhase`'s `idle`:
|
|
132
|
+
* "runs unbounded, so it never yields the channel at all." It buys nothing new by itself —
|
|
133
|
+
* `INDEPENDENT_PHASES` exempts only `entrance|state` and `exit|state`, so `idle` still collides with
|
|
134
|
+
* everything including another `idle`, which is correct: `carousel-3d, flip-in-x` really would fight
|
|
135
|
+
* over the `transform` shorthand (`CHANNEL.skew`) for the ring's whole lifetime, and there is no
|
|
136
|
+
* turn-taking to model. It replaces "nothing is known" with the actual, checked fact.
|
|
137
|
+
*
|
|
138
|
+
* None of the four declares `transitions`: nothing here renders through a CSS `transition:` on the
|
|
139
|
+
* host box for `phaseOf` to derive `state` from — the ring is `renderer: 'javascript'` throughout.
|
|
140
|
+
*/
|
|
141
|
+
export declare const CAROUSEL_PRESETS: Preset[];
|
|
142
|
+
/**
|
|
143
|
+
* Register the spatial carousel.
|
|
144
|
+
*
|
|
145
|
+
* @param registry - Registry to populate.
|
|
146
|
+
* @returns The same registry, for chaining.
|
|
147
|
+
* @complexity O(n) time in the number of primitives and presets.
|
|
148
|
+
*/
|
|
149
|
+
export declare function registerCarousel(registry: Registry): Registry;
|
|
@@ -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 DISCRETE_PRIMITIVES: Primitive[];
|
|
4
|
+
export declare const DISCRETE_PRESETS: Preset[];
|
|
5
|
+
/**
|
|
6
|
+
* Register the `@starting-style` discrete-open catalog (section J).
|
|
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 registerDiscrete(registry: Registry): Registry;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Preset, Primitive } from '../../core/types.js';
|
|
2
|
+
import type { Registry } from '../../core/registry.js';
|
|
3
|
+
export declare const PROXIMITY_FIELD_PRIMITIVES: Primitive[];
|
|
4
|
+
export declare const PROXIMITY_FIELD_PRESETS: Preset[];
|
|
5
|
+
export declare const PROXIMITY_GLOW_PRIMITIVES: Primitive[];
|
|
6
|
+
export declare const PROXIMITY_GLOW_PRESETS: Preset[];
|
|
7
|
+
/** Both halves of the family, for `registerInteractionProximity` below. */
|
|
8
|
+
export declare const INTERACTION_PROXIMITY_PRIMITIVES: Primitive[];
|
|
9
|
+
export declare const INTERACTION_PROXIMITY_PRESETS: Preset[];
|
|
10
|
+
/**
|
|
11
|
+
* Register the cross-element pointer-proximity family.
|
|
12
|
+
*
|
|
13
|
+
* A standalone top-level registration called directly from `catalog/index.ts`, the same shape
|
|
14
|
+
* `registerInteraction`'s own doc comment describes for `registerGestures`/`registerThreeD` —
|
|
15
|
+
* deliberately not folded into `interaction.ts`'s `INTERACTION_PRIMITIVES`/`_PRESETS`, which sits on
|
|
16
|
+
* that file's own 400-line lint ceiling and is being edited by another session concurrently with
|
|
17
|
+
* this one.
|
|
18
|
+
*
|
|
19
|
+
* @param registry - Registry to populate.
|
|
20
|
+
* @returns The same registry, for chaining.
|
|
21
|
+
* @complexity O(n) time in registered primitives and presets.
|
|
22
|
+
* @overallScore 100
|
|
23
|
+
*/
|
|
24
|
+
export declare function registerInteractionProximity(registry: Registry): Registry;
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import type { Preset, Primitive } from '../../core/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The masked label swap: two stacked copies of a label in a clipped box, one sliding out as the
|
|
4
|
+
* other slides in.
|
|
5
|
+
*
|
|
6
|
+
* ### Markup
|
|
7
|
+
*
|
|
8
|
+
* ```html
|
|
9
|
+
* <button data-kui="masked-label-swap">
|
|
10
|
+
* <span data-kui-swap="from">Download</span>
|
|
11
|
+
* <span data-kui-swap="to">Get the file</span>
|
|
12
|
+
* </button>
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Both copies are real elements with real text, not `::before`/`content` — a pseudo-element's
|
|
16
|
+
* generated content cannot be selected, cannot be copied, cannot hold markup, and reaches assistive
|
|
17
|
+
* technology inconsistently. For an effect whose entire subject is a *label*, that is the wrong
|
|
18
|
+
* trade at any price, and it would also have burned one of the two contended pseudo boxes (see
|
|
19
|
+
* `border-draw`'s channel note in `interaction.ts`).
|
|
20
|
+
*
|
|
21
|
+
* `from`/`to` rather than document order, so the pair reads the way a keyframe does and so an icon
|
|
22
|
+
* or a `<span class="sr-only">` sitting between the two copies cannot silently reassign the roles.
|
|
23
|
+
*
|
|
24
|
+
* ### Why one effect covers two patterns
|
|
25
|
+
*
|
|
26
|
+
* The "old number slides out, upgraded number slides in" case is the same mechanism with numerals
|
|
27
|
+
* in the two parts, so it needs no name of its own. What it does need is the `-y` axis, which is
|
|
28
|
+
* why vertical is the unsuffixed default: a price or a count changing reads as a vertical roll,
|
|
29
|
+
* where a call-to-action changing its wording reads better horizontally.
|
|
30
|
+
*
|
|
31
|
+
* ### Channels
|
|
32
|
+
*
|
|
33
|
+
* `discrete` because the host rule pins `display: inline-grid` to stack the two copies in one grid
|
|
34
|
+
* cell — the same reason `split-flap`, `feedback-spin` and six others declare it (`display` is
|
|
35
|
+
* tracked as one physical property regardless of the value written into it).
|
|
36
|
+
*
|
|
37
|
+
* `label-swap` is the box token for the marked parts, exactly parallel to `group-dim`'s `group`:
|
|
38
|
+
* the properties this effect actually animates live on a *child* box, so filing them under
|
|
39
|
+
* `translate` would make the compiler refuse `data-kui="lift, masked-label-swap"` — a button that
|
|
40
|
+
* rises on hover while its label swaps — even though the two never touch the same box. Two
|
|
41
|
+
* `masked-label-swap*` names on one host *are* refused, correctly: they share this primitive, so
|
|
42
|
+
* they collide on every channel it declares, and they would fight over one `--kui-label-swap-dx`.
|
|
43
|
+
*/
|
|
44
|
+
export declare const LABEL_SWAP_PRIMITIVES: Primitive[];
|
|
45
|
+
/**
|
|
46
|
+
* Three axes, three names, one primitive — so all three share the `--kui-label-swap-*` timing
|
|
47
|
+
* namespace, the schema, the reduced-motion policy and the channel claim, and differ only in the
|
|
48
|
+
* two offset custom properties their own selector sets in `interaction.css`.
|
|
49
|
+
*
|
|
50
|
+
* No `transitions` field. `Preset.transitions` describes properties a preset eases *on its own host
|
|
51
|
+
* box*, and the compiler spends them on the one `--kui-transition` property `base.css` applies to
|
|
52
|
+
* the element carrying `data-kui-fx` — which here is the clip box, which does not move. The parts'
|
|
53
|
+
* `translate` transition is authored directly on their own rule, the same way `group-dim`'s
|
|
54
|
+
* children's is, and it is safe there for the same reason: nothing but this primitive can write to
|
|
55
|
+
* `[data-kui-swap]`, because a second effect that did would be on the `label-swap` channel and
|
|
56
|
+
* refused at compile time before it got there.
|
|
57
|
+
*
|
|
58
|
+
* No `requiresOwnSubtree` either, and that is load-bearing rather than an omission — see
|
|
59
|
+
* `INHERITED_STATE` above for why these rules deliberately carry no combinator, and what `target:`
|
|
60
|
+
* buys once they do not.
|
|
61
|
+
*/
|
|
62
|
+
export declare const LABEL_SWAP_PRESETS: Preset[];
|
|
63
|
+
/**
|
|
64
|
+
* Hover intent: nothing happens until the pointer has *rested* for a second, and leaving early
|
|
65
|
+
* cancels it outright.
|
|
66
|
+
*
|
|
67
|
+
* ```html
|
|
68
|
+
* <button data-kui="hover-intent" aria-describedby="t1">Archive
|
|
69
|
+
* <span data-kui-hint id="t1" role="tooltip">Moves to Archive; nothing is deleted</span>
|
|
70
|
+
* </button>
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* ### Why this is timing syntax and not a JavaScript primitive
|
|
74
|
+
*
|
|
75
|
+
* The question was live, and the answer turns on one fact about CSS transitions that is easy to
|
|
76
|
+
* assume wrongly: **a `transition-delay` is not a timer that fires regardless.** The transition's
|
|
77
|
+
* parameters are read from the element's *after-change* style, so when the pointer leaves before
|
|
78
|
+
* the delay elapses, the property reverts, the pending transition is cancelled before it ever
|
|
79
|
+
* started, and nothing at all was drawn. Cancel-on-early-leave — the half of hover intent that a
|
|
80
|
+
* plain delay supposedly cannot express — is native behaviour, not something a script has to
|
|
81
|
+
* supply. This file's `INHERITED_STATE` note explains the one extra step needed to get that
|
|
82
|
+
* asymmetric delay onto a *second* box.
|
|
83
|
+
*
|
|
84
|
+
* The claim that `delay:` "starts its clock on the trigger and never cancels" is true, but of the
|
|
85
|
+
* other tier: the library's own `on:hover` activation listens for `pointerenter`/`focusin` and has
|
|
86
|
+
* no un-trigger (`core/activation.ts`), so a JS-activated effect really does keep its appointment
|
|
87
|
+
* after the pointer has gone. That is a property of the activation, not of `delay:`, and the fix
|
|
88
|
+
* for it is not a new primitive here.
|
|
89
|
+
*
|
|
90
|
+
* So the only thing genuinely missing was something *to* delay: section I had twenty-five names
|
|
91
|
+
* that decorate the trigger and not one that reveals a companion, so `delay:` had nothing to be
|
|
92
|
+
* hover intent *about*. This preset is that missing subject, and it is thirty lines of stylesheet.
|
|
93
|
+
*
|
|
94
|
+
* A JavaScript primitive would be the right answer for a different feature — **trajectory** intent,
|
|
95
|
+
* the mega-menu heuristic that keeps a panel open while the pointer is moving *toward* it even
|
|
96
|
+
* though it has technically left. That needs pointer history and a predicted path; no amount of
|
|
97
|
+
* `transition-delay` expresses it, and it should be built when someone wants it rather than
|
|
98
|
+
* smuggled in as the implementation of this one.
|
|
99
|
+
*
|
|
100
|
+
* ### Channels
|
|
101
|
+
*
|
|
102
|
+
* `hint`, its own box token, for the reason `group-dim` gives for `group`: the properties animate a
|
|
103
|
+
* child box, so filing them under `opacity` would refuse `data-kui="fade-in, hover-intent"` — a
|
|
104
|
+
* card that fades itself in and reveals a hint on rest — even though the two never touch the same
|
|
105
|
+
* box.
|
|
106
|
+
*
|
|
107
|
+
* The hint is `pointer-events: none` in every state, deliberately. A revealed panel you can move
|
|
108
|
+
* the pointer *into* is a menu, not a hint: it needs the pointer to be able to leave the trigger
|
|
109
|
+
* without closing it, which is the trajectory problem above. Naming this one non-interactive keeps
|
|
110
|
+
* it honest about which of the two it is.
|
|
111
|
+
*/
|
|
112
|
+
export declare const HOVER_INTENT_PRIMITIVES: Primitive[];
|
|
113
|
+
export declare const HOVER_INTENT_PRESETS: Preset[];
|
|
114
|
+
/**
|
|
115
|
+
* A name tag springing out from an avatar on hover, and the same mechanism used to hover a word
|
|
116
|
+
* and pop out an image beside it — one composite, four placements, not two effects.
|
|
117
|
+
*
|
|
118
|
+
* ### Markup
|
|
119
|
+
*
|
|
120
|
+
* ```html
|
|
121
|
+
* <span data-kui="anchored-preview" class="avatar-wrap" tabindex="0">
|
|
122
|
+
* <img class="avatar" src="jane.jpg" alt="">
|
|
123
|
+
* <span data-kui-preview class="name-tag">Jane Doe</span>
|
|
124
|
+
* </span>
|
|
125
|
+
*
|
|
126
|
+
* <span data-kui="anchored-preview-right" tabindex="0">a word
|
|
127
|
+
* <img data-kui-preview src="preview.jpg" alt="">
|
|
128
|
+
* </span>
|
|
129
|
+
* ```
|
|
130
|
+
*
|
|
131
|
+
* The trigger has to be its own focusable, hoverable element — an `<img>` cannot contain the
|
|
132
|
+
* preview, which is why the avatar case wraps both in a `<span tabindex="0">` rather than putting
|
|
133
|
+
* `data-kui` on the `<img>` itself. That is a real markup cost worth naming, and it is the same one
|
|
134
|
+
* `hover-intent`'s own doc comment already pays for a tooltip's trigger.
|
|
135
|
+
*
|
|
136
|
+
* ### Why not CSS anchor positioning
|
|
137
|
+
*
|
|
138
|
+
* This is the textbook use case `anchor()`/`position-anchor` was built for, and it was considered.
|
|
139
|
+
* The project's own planning notes record the owner dropping anchor positioning outright on
|
|
140
|
+
* 2026-08-26, for a reason that is decisive here specifically: it is the only one of the "four
|
|
141
|
+
* modern CSS techniques" listed there with a
|
|
142
|
+
* *harmful* fallback — an unsupported `anchor()` puts the element in the *wrong* place rather than
|
|
143
|
+
* a neutral one — and the two conditions set for reopening it (proof that an anchored position
|
|
144
|
+
* transitions rather than snaps when the anchor moves, and materially better support than the
|
|
145
|
+
* measured 84.12%) are both still open. So this reuses the mechanism `hover-intent` and
|
|
146
|
+
* `masked-label-swap` already ship instead: the trigger's state and geometry travel as *inherited
|
|
147
|
+
* custom properties*, computed once per placement and read by a genuinely generic standalone rule.
|
|
148
|
+
* That mechanism needs no feature detection and no fallback branch, because it already works in
|
|
149
|
+
* every browser the rest of the catalog does.
|
|
150
|
+
*
|
|
151
|
+
* ### Why four preset names and not a `placement:` parameter
|
|
152
|
+
*
|
|
153
|
+
* The same reasoning `label-swap`'s own doc comment gives for its axis: CSS cannot branch on a
|
|
154
|
+
* custom property's *value* without `@container style()`, which is not portable enough to build a
|
|
155
|
+
* shipped effect on. A `placement:` keyword would have to be read by JavaScript, turning a pure
|
|
156
|
+
* stylesheet effect into a JS one for the sake of one word. Four preset names cost nothing extra —
|
|
157
|
+
* they share this one primitive's schema, timing namespace, reduced-motion policy and channel
|
|
158
|
+
* claim — and it is how the rest of the catalog already spells "one mechanism, several fixed
|
|
159
|
+
* shapes" (`masked-label-swap-x`/`-diagonal`, `card-flip-x`/`-y`, `flip-in-x`/`-y`).
|
|
160
|
+
*
|
|
161
|
+
* `anchored-preview` (no suffix) is `top`, matching `hover-intent`'s own default placement for the
|
|
162
|
+
* same reason: a name tag or a tooltip reads naturally above its trigger unless told otherwise.
|
|
163
|
+
*
|
|
164
|
+
* ### How placement reaches a standalone child rule without a combinator
|
|
165
|
+
*
|
|
166
|
+
* Each placement preset's own base rule (no state involved) sets two custom properties on
|
|
167
|
+
* *itself* — `--kui-anchored-preview-inset` (the four-value `inset` shorthand for that side) and
|
|
168
|
+
* `--kui-anchored-preview-travel` (the `translate` value combining the constant centering offset on
|
|
169
|
+
* the cross-axis with the spring distance on the main axis, sign and axis already resolved). Both
|
|
170
|
+
* are precomputed per placement so `[data-kui-preview]` itself stays fully generic: `position:
|
|
171
|
+
* absolute; inset: var(--kui-anchored-preview-inset); translate: var(--kui-anchored-preview-travel)`
|
|
172
|
+
* and nothing placement-specific. No rule anywhere carries a combinator after an fx compound, so
|
|
173
|
+
* `anchored-preview` never enters the reaching-selector set `css-requires-own-subtree.test.ts`
|
|
174
|
+
* derives, needs no `requiresOwnSubtree`, and `target:` relocates all four cleanly.
|
|
175
|
+
*
|
|
176
|
+
* ### Channels
|
|
177
|
+
*
|
|
178
|
+
* `preview`, its own box token, for the reason `hint` and `label-swap` already state: the
|
|
179
|
+
* properties animate a child box, so filing them under `opacity`/`translate`/`scale` would refuse
|
|
180
|
+
* `data-kui="lift, anchored-preview"` — an avatar that lifts on hover while its name tag pops out —
|
|
181
|
+
* even though the two never touch the same box. No pseudo-element is used at all (the tag/image is
|
|
182
|
+
* real, selectable, copyable markup, the same reasoning `masked-label-swap`'s doc comment gives for
|
|
183
|
+
* its own two copies), so this effect has zero pseudo-element collision cost.
|
|
184
|
+
*
|
|
185
|
+
* The preview is `pointer-events: none` in every state, for the identical reason `hover-intent`'s
|
|
186
|
+
* hint is: a revealed panel the pointer can move *into* is a menu, which needs pointer-history
|
|
187
|
+
* "trajectory intent" rather than this mechanism, and should be built when someone wants it rather
|
|
188
|
+
* than smuggled in here.
|
|
189
|
+
*/
|
|
190
|
+
export declare const ANCHORED_PREVIEW_PRIMITIVES: Primitive[];
|
|
191
|
+
export declare const ANCHORED_PREVIEW_PRESETS: Preset[];
|
|
192
|
+
/**
|
|
193
|
+
* A search icon that grows into a full input field.
|
|
194
|
+
*
|
|
195
|
+
* ### Markup
|
|
196
|
+
*
|
|
197
|
+
* ```html
|
|
198
|
+
* <label data-kui="search-expand">
|
|
199
|
+
* <svg aria-hidden="true">…</svg>
|
|
200
|
+
* <input data-kui-search-field type="search" placeholder="Search…">
|
|
201
|
+
* </label>
|
|
202
|
+
* ```
|
|
203
|
+
*
|
|
204
|
+
* The host is a `<label>`, not a `<div>`, and that is load-bearing rather than a styling
|
|
205
|
+
* preference: clicking anywhere in a `<label>` — including the icon — natively focuses the
|
|
206
|
+
* `<input>` it contains, with no JavaScript and no matching `for`/`id` pair, which is what lets a
|
|
207
|
+
* collapsed, icon-only control still be a single click away from typing.
|
|
208
|
+
*
|
|
209
|
+
* ### Why this is not a FLIP job
|
|
210
|
+
*
|
|
211
|
+
* A reviewer ruled on this ahead of time (see this task's brief): a width/clip/intrinsic-size
|
|
212
|
+
* transition is sufficient unless the field genuinely *relocates* between two layouts, and this one
|
|
213
|
+
* does not — it grows in place. So the host's own `inline-size` is an ordinary, explicit-length CSS
|
|
214
|
+
* transition (`Preset.transitions`, never a bare stylesheet `transition:` — seeing one there is
|
|
215
|
+
* itself a failure `css-composition-invariants.test.ts`'s "transition channel" suite asserts
|
|
216
|
+
* against), not a measure-before/measure-after/invert-and-play choreography. Both `collapsed` and
|
|
217
|
+
* `width` are explicit lengths for the same reason `interpolate-size: allow-keywords` is not used
|
|
218
|
+
* here at all: the project's own planning notes record that technique as deliberately deferred
|
|
219
|
+
* pending Safari support, and reaching for it on a brand new effect would be an unsanctioned first
|
|
220
|
+
* use of something the owner has already decided to wait on.
|
|
221
|
+
*
|
|
222
|
+
* ### How the input's fade reaches a standalone child rule without a combinator
|
|
223
|
+
*
|
|
224
|
+
* The same `INHERITED_STATE` mechanism `hover-intent` and `anchored-preview` (above) use, for the
|
|
225
|
+
* identical reason: `[data-kui-fx~='search-expand'] input` would be a combinator after the fx
|
|
226
|
+
* compound, forcing `requiresOwnSubtree` and making `target:` a no-op — and unlike `group-dim`,
|
|
227
|
+
* there is no reason to want that here. So the host's `:focus-visible`/`:has(:focus-visible)`/
|
|
228
|
+
* `:hover`/`:has(input:not(:placeholder-shown))` rules set `--kui-search-expand-shown` (0/1) and
|
|
229
|
+
* `--kui-search-expand-lag` (0ms at rest, the authored delay once expanded — the same enter-only
|
|
230
|
+
* asymmetry every hover-intent-shaped rule in this file uses), and the marked field reads both from
|
|
231
|
+
* a *standalone* `[data-kui-search-field]` selector with no fx
|
|
232
|
+
* prefix at all.
|
|
233
|
+
*
|
|
234
|
+
* ### Channels
|
|
235
|
+
*
|
|
236
|
+
* Three, because this primitive owns three distinct boxes' worth of properties: `expand` for the
|
|
237
|
+
* host's own `inline-size` (a channel of its own, the same reasoning `skew`/`border` document —
|
|
238
|
+
* no existing channel names this physical property, and folding it into `layout` would couple this
|
|
239
|
+
* effect's fate to `header-shrink`'s unrelated `padding-block`/`font-size` claim); `discrete`
|
|
240
|
+
* because the host pins `display: inline-flex` unconditionally, the same reason `split-flap` and
|
|
241
|
+
* `masked-label-swap` declare it; and `search-field` for the inherited signals that feed the child
|
|
242
|
+
* input's `opacity`, the same reasoning `hint`/`label-swap` give for their own child-box channels.
|
|
243
|
+
*/
|
|
244
|
+
export declare const SEARCH_EXPAND_PRIMITIVES: Primitive[];
|
|
245
|
+
export declare const SEARCH_EXPAND_PRESETS: Preset[];
|
|
246
|
+
/** All four reveal-shaped families, for `interaction.ts`'s catalog-wide exports. */
|
|
247
|
+
export declare const REVEAL_PRIMITIVES: Primitive[];
|
|
248
|
+
export declare const REVEAL_PRESETS: Preset[];
|
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
* `cursor-*` family) — kept separate from DOM/timer wiring so the position → transform arithmetic
|
|
4
4
|
* is assertable without a browser, the same separation `text-shared.ts` uses for its state
|
|
5
5
|
* machines.
|
|
6
|
+
*
|
|
7
|
+
* `HOVER_TRANSITIONS` at the bottom is an unrelated second concern sharing this file only because
|
|
8
|
+
* `interaction.ts` hit its own 400-line lint ceiling — the same reason `css-invariants.test.ts`
|
|
9
|
+
* split into `css-scan.ts`/`channel-properties.ts`, not a claim that a lookup table belongs beside
|
|
10
|
+
* pointer-tracking arithmetic.
|
|
6
11
|
*/
|
|
12
|
+
import type { ParameterSchema, TransitionSegment } from '../../core/types.js';
|
|
7
13
|
export interface TiltAngles {
|
|
8
14
|
rotateX: number;
|
|
9
15
|
rotateY: number;
|
|
@@ -54,3 +60,39 @@ export declare function parallaxOffset(point: LocalPoint, size: ElementSize, str
|
|
|
54
60
|
* @overallScore 100
|
|
55
61
|
*/
|
|
56
62
|
export declare function supportsFineHover(win: Window): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Transition segments for the five hover-family presets whose motion is a bare host-rule
|
|
65
|
+
* `transition:` in `interaction.css` (`lift`, `pop`, `lift-shadow`, `border-draw`, `border-glow`).
|
|
66
|
+
* The other eight — `shine-sweep`, `split-flap`, `beam-border`, `underline-slide`,
|
|
67
|
+
* `underline-center`, `icon-wiggle`, `icon-spin`, `icon-bounce` — animate via a keyframed
|
|
68
|
+
* `animation:` on `:hover`/`:focus-visible`, or transition a pseudo-element (`::after`) rather than
|
|
69
|
+
* the host box `Preset.transitions` describes, so they declare none here.
|
|
70
|
+
*
|
|
71
|
+
* A lookup keyed by primitive id, not a parallel array, so a typo in one list can't silently pair
|
|
72
|
+
* the wrong segments with the wrong preset — `HOVER_PRESETS` (`interaction.ts`) still derives its
|
|
73
|
+
* name/primitive pair from `HOVER_PRIMITIVES` the same way it always has.
|
|
74
|
+
*/
|
|
75
|
+
export declare const HOVER_TRANSITIONS: Partial<Record<string, TransitionSegment[]>>;
|
|
76
|
+
/**
|
|
77
|
+
* The hover names whose motion is a host-level `animation:` in `interaction.css` rather than a
|
|
78
|
+
* transition or a pseudo-element rule — `Preset.delivery`'s one declared value, applied by
|
|
79
|
+
* `HOVER_PRESETS`.
|
|
80
|
+
*
|
|
81
|
+
* Beside {@link HOVER_TRANSITIONS} because it is the same shape of fact about the same family: a
|
|
82
|
+
* per-name detail of how that name's CSS is written, which `HOVER_PRIMITIVES` cannot carry because
|
|
83
|
+
* one primitive backs names on both sides of it.
|
|
84
|
+
*
|
|
85
|
+
* Written out rather than derived, because there is nothing in the effect record to derive it from:
|
|
86
|
+
* `HOVER_TRANSITIONS` does not separate them (`shine-sweep`, `beam-border`, `underline-slide` and
|
|
87
|
+
* `underline-center` are absent from it too, and are safe — they animate a pseudo-element that
|
|
88
|
+
* inline style cannot reach), and neither does the channel list, the renderer, or the parameter
|
|
89
|
+
* set. The fact lives in the stylesheet, so the stylesheet is what audits it:
|
|
90
|
+
* `test/css-composition-invariants.test.ts` derives the true set from `src/css/*.css` with
|
|
91
|
+
* `extractHostAnimationBindings` and fails in both directions, which is what stops this drifting the
|
|
92
|
+
* first time a fifth hover keyframe is added.
|
|
93
|
+
*/
|
|
94
|
+
export declare const STYLESHEET_ANIMATED_HOVERS: string[];
|
|
95
|
+
export declare const BEAM_PARAMS: ParameterSchema;
|
|
96
|
+
export declare const SHINE_PARAMS: ParameterSchema;
|
|
97
|
+
export declare const COLOR_PARAMS: Record<string, ParameterSchema>;
|
|
98
|
+
export declare const BORDER_DRAW_PARAMS: ParameterSchema;
|