@pieai/swimmer-ui-kit 2.1.0 → 2.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/CHANGELOG.md +106 -0
- package/dist/index.d.ts +115 -11
- package/dist/index.js +4163 -3811
- package/dist/styles.css +1 -1
- package/donors-individual-lock.json +5 -4
- package/donors-individual.md +39 -2
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,112 @@
|
|
|
3
3
|
All notable changes to `@pieai/swimmer-ui-kit`.
|
|
4
4
|
Format: [Keep a Changelog](https://keepachangelog.com); versioning: semver.
|
|
5
5
|
|
|
6
|
+
## 2.2.0 — 2026-09-11
|
|
7
|
+
|
|
8
|
+
Minor, not major: nothing published is removed or renamed, and no `--game-ui-*`
|
|
9
|
+
name changes. Two token **values** change, and every button in every product
|
|
10
|
+
gets a visible lip — deliberately, and described under Changed.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Named liquid forms.** `LIQUID_FORMS` gives the gooey engine six looks with
|
|
15
|
+
names instead of a page of physical knobs: `press`, `settle`, `merge`,
|
|
16
|
+
`follow`, `fill`, `drain`. Each is a tested bundle of blur, contrast, outline
|
|
17
|
+
and spring, resolved through `liquidFormGroup` / `liquidFormItem`, which
|
|
18
|
+
merge overrides into a form rather than replacing it.
|
|
19
|
+
|
|
20
|
+
This layer was missing, and its absence had a measurable cost: the only
|
|
21
|
+
production consumer had to hand-tune 166 lines around a normal button to get
|
|
22
|
+
one liquid control, and the corrections it discovered never came back here.
|
|
23
|
+
|
|
24
|
+
- **`LiquidSurface`.** The packaging for the single-body forms — a
|
|
25
|
+
non-interactive silhouette behind, real DOM in front and never transformed,
|
|
26
|
+
so a pressed control keeps its hit target, its focus ring and its crisp text.
|
|
27
|
+
`merge` and `follow` describe a relationship between siblings and stay with
|
|
28
|
+
`LiquidGroup`, where they belong.
|
|
29
|
+
|
|
30
|
+
- **`GameButton` gains `surface="flat" | "liquid"`.** Deliberately a second
|
|
31
|
+
axis rather than a new `variant`: `variant` is a tone, and folding 'liquid'
|
|
32
|
+
into it would have made the brand's signature surface mutually exclusive with
|
|
33
|
+
saying "this action is destructive". The default path renders byte-identical
|
|
34
|
+
markup — asserted in a test — so no existing call site moves.
|
|
35
|
+
|
|
36
|
+
- **`blob`: an organic silhouette drawn as geometry.** `LiquidGroup.Item`
|
|
37
|
+
takes a `blob` shape, and the single-body forms use it. The outline swells
|
|
38
|
+
outward from the control's own box along a closed Catmull-Rom spline, so it
|
|
39
|
+
is exact at every device ratio and zoom, and it is **outward-only** by
|
|
40
|
+
construction — no amplitude can push the surface inside a label's padding or
|
|
41
|
+
across a hit target.
|
|
42
|
+
|
|
43
|
+
This replaces `waviness` as the way a form gets an irregular edge, and the
|
|
44
|
+
reason is a measurement rather than a preference. Chrome resamples
|
|
45
|
+
`feDisplacementMap` with nearest-neighbour, so a displaced contour can only
|
|
46
|
+
land on whole pixels. Captured at device ratio 1 with alpha recovered from
|
|
47
|
+
two backgrounds and sampled along a straight edge, the settings shipped in
|
|
48
|
+
the 2.2.0 development line moved the outline by a constant 1.5px and varied
|
|
49
|
+
it by 0.01px across the whole side: there was no wave at all, which is
|
|
50
|
+
exactly how it looked. Raising the frequency does not fix it — it trades one
|
|
51
|
+
whole-side step for per-pixel jitter, which is the frayed edge the effect was
|
|
52
|
+
blamed for in the first place.
|
|
53
|
+
|
|
54
|
+
`waviness` and `wavinessFreq` remain exported, documented and unchanged for
|
|
55
|
+
callers who want the filter-side texture on large merging bodies, which is
|
|
56
|
+
what the donor uses it for.
|
|
57
|
+
|
|
58
|
+
- **`gloss`.** Interior volume for a liquid body, from `feSpecularLighting` at
|
|
59
|
+
a grazing 22° elevation. An irregular outline around a flat fill still reads
|
|
60
|
+
as a sticker; a surface reads as a material through how it catches light. The
|
|
61
|
+
light is distant rather than positional so it does not have to be recomputed
|
|
62
|
+
from a measured box on every resize.
|
|
63
|
+
|
|
64
|
+
- **`LIQUID_GOOEY_MIN_EDGE_RAMP` and `liquidGooeyEdgeContrast`.** The floor on
|
|
65
|
+
how narrow the goo threshold may leave an edge.
|
|
66
|
+
|
|
67
|
+
### Changed
|
|
68
|
+
|
|
69
|
+
- **Buttons have a lip.** `--game-ui-button-lip-ink` and
|
|
70
|
+
`--game-ui-button-lip-depth` add a solid, unblurred band under
|
|
71
|
+
`.game-ui-button`, and a press travels its height instead of only scaling in
|
|
72
|
+
place. This is what separates a game control from a card: a key has a height
|
|
73
|
+
you can see, and pressing it has somewhere to go.
|
|
74
|
+
|
|
75
|
+
**Blast radius: every button in every product using the kit.** Only
|
|
76
|
+
`.game-ui-button` is affected — tabs, toggles and segmented options live
|
|
77
|
+
inside other surfaces and keep their flat treatment — and `ghost` and
|
|
78
|
+
`disabled` opt out. A product that wants the old look sets
|
|
79
|
+
`--game-ui-button-lip-depth: 0px`.
|
|
80
|
+
|
|
81
|
+
- **The goo threshold has a minimum edge width.** `contrast` is not a look: the
|
|
82
|
+
alpha crossing sits at a fixed 5/12 of the ramp, so contrast decides only how
|
|
83
|
+
many pixels wide the edge is. Blur 4 with contrast 24 — the pairing the one
|
|
84
|
+
production consumer had converged on — works out at 0.43px, an edge thinner
|
|
85
|
+
than the pixel drawing it, which is the definition of an aliased one.
|
|
86
|
+
`liquidGooeyEdgeContrast` now lowers contrast until the ramp is at least
|
|
87
|
+
1.3px, the width at which measured contour roughness bottoms out at the score
|
|
88
|
+
of a shape with no displacement at all.
|
|
89
|
+
|
|
90
|
+
This cannot move a silhouette, only soften how it is drawn, and it only
|
|
91
|
+
engages where an edge was too thin. `merge` and the other soft forms are
|
|
92
|
+
untouched.
|
|
93
|
+
|
|
94
|
+
- **Resting `waviness` is 0 by default.** `--game-ui-liquid-gooey-waviness` was
|
|
95
|
+
6 and is now 0; the SSR fallback matches. 6 came from the donor and meant the
|
|
96
|
+
out-of-the-box look was a permanently undulating outline, which reads as a
|
|
97
|
+
rendering defect on a static rounded rectangle — University cancelled it with
|
|
98
|
+
`waviness={0}` on all eighteen surfaces that use liquid, and those overrides
|
|
99
|
+
are now unnecessary.
|
|
100
|
+
|
|
101
|
+
**Migration:** none required. A surface that genuinely wants the old molten
|
|
102
|
+
edge passes `waviness={6}`.
|
|
103
|
+
|
|
104
|
+
- **Removed before release: `LIQUID_REST_EDGE_SLOPE_MAX` and
|
|
105
|
+
`liquidRestEdgeSlope`.** Added earlier in this same unreleased line, on the
|
|
106
|
+
theory that a resting edge tears when amplitude times frequency goes too
|
|
107
|
+
high. The measurement above shows the cap was not protecting the edge — it
|
|
108
|
+
was selecting frequencies low enough for the displacement to stop varying,
|
|
109
|
+
and scoring well because the feature had disappeared. Nothing shipped with
|
|
110
|
+
them, so nothing has to migrate.
|
|
111
|
+
|
|
6
112
|
## 2.1.0 — 2026-09-02
|
|
7
113
|
|
|
8
114
|
### Added
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ButtonHTMLAttributes } from 'react';
|
|
2
|
+
import { CSSProperties } from 'react';
|
|
2
3
|
import { ForwardRefExoticComponent } from 'react';
|
|
3
4
|
import { HTMLAttributes } from 'react';
|
|
4
5
|
import { InputHTMLAttributes } from 'react';
|
|
@@ -16,6 +17,17 @@ export declare interface BendTuning {
|
|
|
16
17
|
horizontal?: number;
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
declare interface BlobShape {
|
|
21
|
+
/** Outward bulge, in px. 0 gives the plain rounded rectangle. */
|
|
22
|
+
readonly amplitude: number;
|
|
23
|
+
/** Which shape. Same seed, same silhouette, every render. */
|
|
24
|
+
readonly seed?: number;
|
|
25
|
+
/** How many swells go round the outline. 2 is lazy, 5 is busy. */
|
|
26
|
+
readonly lobes?: number;
|
|
27
|
+
/** Rotates the swells around the outline; the knob a press animates. */
|
|
28
|
+
readonly phase?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
19
31
|
export declare const CLAY_ASSET_BASE_PATH: "/assets/game/ui/clay/phase03-clay-kit";
|
|
20
32
|
|
|
21
33
|
export declare const CLAY_ASSET_SIZE_TOKENS: {
|
|
@@ -856,16 +868,6 @@ export declare interface DissolveOptions {
|
|
|
856
868
|
|
|
857
869
|
export declare type DissolveValue = boolean | number | DissolveOptions;
|
|
858
870
|
|
|
859
|
-
/**
|
|
860
|
-
* Morph shape physics adapted from `liquid-gooey` by Jakub Antalik.
|
|
861
|
-
*
|
|
862
|
-
* Source: https://github.com/Jakubantalik/Libraries/tree/main/packages/liquid-gooey
|
|
863
|
-
* Pinned commit: 3862ffa345217443b63696a8c331a0664eea4b04
|
|
864
|
-
* Copyright (c) 2026 Jakub Antalik. Licensed under the MIT License.
|
|
865
|
-
* See NOTICE for the attribution and license text. This local module keeps
|
|
866
|
-
* the donor's centre -> size -> corner timeline and content cross-blur, but
|
|
867
|
-
* routes every tunable value through SwimmerUIKit's token layer.
|
|
868
|
-
*/
|
|
869
871
|
declare interface EvolveOptions {
|
|
870
872
|
/** Spring driving the liquid mass's centre. */
|
|
871
873
|
massStiffness?: number;
|
|
@@ -1374,16 +1376,24 @@ export declare interface GameBuildLibraryProps {
|
|
|
1374
1376
|
'data-testid'?: string | undefined;
|
|
1375
1377
|
}
|
|
1376
1378
|
|
|
1377
|
-
export declare function GameButton({ children, className, onClick, sound, static: isStatic, type, variant, ...props }: GameButtonProps): ReactNode;
|
|
1379
|
+
export declare function GameButton({ children, className, onClick, sound, static: isStatic, surface, type, variant, ...props }: GameButtonProps): ReactNode;
|
|
1378
1380
|
|
|
1379
1381
|
export declare interface GameButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
1380
1382
|
children: ReactNode;
|
|
1381
1383
|
sound?: GameInteractionSoundOptions | false;
|
|
1382
1384
|
/** Disable the scale-on-press feedback where the motion would distract. */
|
|
1383
1385
|
static?: boolean;
|
|
1386
|
+
/**
|
|
1387
|
+
* Draw the button on a liquid body. The button itself is unchanged — same
|
|
1388
|
+
* element, same classes, same hit target; a silhouette behind it does the
|
|
1389
|
+
* deforming, so text stays crisp and the tap target never shrinks.
|
|
1390
|
+
*/
|
|
1391
|
+
surface?: GameButtonSurface;
|
|
1384
1392
|
variant?: GameButtonVariant;
|
|
1385
1393
|
}
|
|
1386
1394
|
|
|
1395
|
+
export declare type GameButtonSurface = 'flat' | 'liquid';
|
|
1396
|
+
|
|
1387
1397
|
export declare type GameButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger' | 'success';
|
|
1388
1398
|
|
|
1389
1399
|
/**
|
|
@@ -2330,6 +2340,11 @@ export declare interface ImageMeltOptions {
|
|
|
2330
2340
|
waviness?: number;
|
|
2331
2341
|
}
|
|
2332
2342
|
|
|
2343
|
+
/** Every form name, for shelves, docs and exhaustiveness checks. */
|
|
2344
|
+
export declare const LIQUID_FORM_NAMES: readonly LiquidForm[];
|
|
2345
|
+
|
|
2346
|
+
export declare const LIQUID_FORMS: Readonly<Record<LiquidForm, LiquidFormSpec>>;
|
|
2347
|
+
|
|
2333
2348
|
/**
|
|
2334
2349
|
* The default fraction is intentionally derived from the rendered surface,
|
|
2335
2350
|
* not from the requested waviness token. A fixed pixel displacement is safe
|
|
@@ -2337,6 +2352,63 @@ export declare interface ImageMeltOptions {
|
|
|
2337
2352
|
*/
|
|
2338
2353
|
export declare const LIQUID_GOOEY_WAVINESS_MAX_FRACTION = 0.3;
|
|
2339
2354
|
|
|
2355
|
+
/**
|
|
2356
|
+
* The named looks, ordered by how much cohesion the liquid is losing:
|
|
2357
|
+
* `press` barely deforms, `drain` stops being a shape at all.
|
|
2358
|
+
*/
|
|
2359
|
+
export declare type LiquidForm = 'press' | 'settle' | 'merge' | 'follow' | 'fill' | 'drain';
|
|
2360
|
+
|
|
2361
|
+
/** Filter-level knobs a form fixes on the group. */
|
|
2362
|
+
export declare interface LiquidFormGroup {
|
|
2363
|
+
readonly blur: number;
|
|
2364
|
+
readonly contrast: number;
|
|
2365
|
+
/**
|
|
2366
|
+
* How far the outline swells outward from the control's box, in px. The
|
|
2367
|
+
* bulge is outward-only, so this never eats into a label's padding, and it
|
|
2368
|
+
* is clamped to 18% of the shorter side so one number suits a 44px button
|
|
2369
|
+
* and a 14px meter.
|
|
2370
|
+
*/
|
|
2371
|
+
readonly blob: number;
|
|
2372
|
+
/** How many swells go round the outline. 2 is lazy, 5 is busy. */
|
|
2373
|
+
readonly lobes: number;
|
|
2374
|
+
/**
|
|
2375
|
+
* Volume. The edge says 「liquid」 only as an outline; this is what makes the
|
|
2376
|
+
* inside of the body look like a material instead of a flat sticker.
|
|
2377
|
+
*/
|
|
2378
|
+
readonly gloss: number;
|
|
2379
|
+
readonly filterPadding: number;
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
/**
|
|
2383
|
+
* Resolve a form into group props, letting a caller override single knobs.
|
|
2384
|
+
*
|
|
2385
|
+
* Overrides are merged rather than replacing the bundle, so a surface that
|
|
2386
|
+
* needs one different value does not have to restate the other three and
|
|
2387
|
+
* silently drift from the form it claims to be using.
|
|
2388
|
+
*/
|
|
2389
|
+
export declare function liquidFormGroup(form: LiquidForm, overrides?: Partial<LiquidFormGroup>): LiquidFormGroup;
|
|
2390
|
+
|
|
2391
|
+
/** Item-level behaviour a form fixes on each participating item. */
|
|
2392
|
+
export declare interface LiquidFormItem {
|
|
2393
|
+
readonly effect?: 'morph' | 'melt' | 'bend';
|
|
2394
|
+
readonly morph?: MorphTuning;
|
|
2395
|
+
readonly transition?: Transition;
|
|
2396
|
+
readonly dissolve?: boolean;
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
/** Resolve a form into item props, with the same merge-don't-replace rule. */
|
|
2400
|
+
export declare function liquidFormItem(form: LiquidForm, overrides?: LiquidFormItem): LiquidFormItem;
|
|
2401
|
+
|
|
2402
|
+
export declare interface LiquidFormSpec {
|
|
2403
|
+
/** One sentence on what a viewer sees, for docs and for picking. */
|
|
2404
|
+
readonly summary: string;
|
|
2405
|
+
readonly group: LiquidFormGroup;
|
|
2406
|
+
readonly item: LiquidFormItem;
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
/** The one-line description of each form, for pickers and documentation. */
|
|
2410
|
+
export declare function liquidFormSummary(form: LiquidForm): string;
|
|
2411
|
+
|
|
2340
2412
|
declare interface LiquidGooeyBudgetOptions {
|
|
2341
2413
|
maxAnimatedGroups?: number;
|
|
2342
2414
|
maxFilterArea?: number;
|
|
@@ -2388,6 +2460,11 @@ export declare interface LiquidGroupProps extends Omit<HTMLAttributes<HTMLDivEle
|
|
|
2388
2460
|
blur?: number;
|
|
2389
2461
|
/** Alpha-contrast slope. Larger values make the liquid edge harder. */
|
|
2390
2462
|
contrast?: number;
|
|
2463
|
+
/**
|
|
2464
|
+
* Volume. 0 leaves the body a flat colour; higher values light it as a
|
|
2465
|
+
* curved surface so it reads as a material rather than as a silhouette.
|
|
2466
|
+
*/
|
|
2467
|
+
gloss?: number;
|
|
2391
2468
|
/** Surface fill. Defaults to the kit's theme surface token. */
|
|
2392
2469
|
fill?: string;
|
|
2393
2470
|
/** Extra filter-region slack in px for the silhouette's painted edges. */
|
|
@@ -2432,6 +2509,12 @@ export declare interface LiquidItemProps extends Omit<HTMLAttributes<HTMLDivElem
|
|
|
2432
2509
|
delay?: number;
|
|
2433
2510
|
/** Override the measured content border radius for the silhouette. */
|
|
2434
2511
|
radius?: number | CornerRadii;
|
|
2512
|
+
/**
|
|
2513
|
+
* Pour the outline outward into an organic body instead of leaving it a
|
|
2514
|
+
* rounded rectangle. The bulge is outward-only, so the silhouette always
|
|
2515
|
+
* contains the content's own box however bold the amplitude gets.
|
|
2516
|
+
*/
|
|
2517
|
+
blob?: BlobShape;
|
|
2435
2518
|
/**
|
|
2436
2519
|
* Select the adopted item surface behavior. Bend follows child geometry.
|
|
2437
2520
|
* Move is a group gesture (`motion="follow"`), not an item effect.
|
|
@@ -2468,6 +2551,27 @@ export declare interface LiquidMetalButtonProps extends ButtonHTMLAttributes<HTM
|
|
|
2468
2551
|
|
|
2469
2552
|
export declare type LiquidMetalRendererMode = 'auto' | 'css' | 'webgl';
|
|
2470
2553
|
|
|
2554
|
+
export declare function LiquidSurface({ children, form, active, fill, stroke, shadow, radius, className, style, }: LiquidSurfaceProps): ReactNode;
|
|
2555
|
+
|
|
2556
|
+
export declare interface LiquidSurfaceProps {
|
|
2557
|
+
children: ReactNode;
|
|
2558
|
+
/** Which named look. Defaults to the press form, the one a control wants. */
|
|
2559
|
+
form?: LiquidForm;
|
|
2560
|
+
/**
|
|
2561
|
+
* Whether the form is engaged: pressed for `press`, landed for `settle`,
|
|
2562
|
+
* leaving for `drain`. Ignored by forms with no engaged state.
|
|
2563
|
+
*/
|
|
2564
|
+
active?: boolean;
|
|
2565
|
+
/** Silhouette paint. Defaults to the kit's raised surface token. */
|
|
2566
|
+
fill?: string;
|
|
2567
|
+
stroke?: string;
|
|
2568
|
+
shadow?: string;
|
|
2569
|
+
/** Corner radius of the body, in px. 999 gives a pill. */
|
|
2570
|
+
radius?: number;
|
|
2571
|
+
className?: string;
|
|
2572
|
+
style?: CSSProperties;
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2471
2575
|
export declare interface MorphTuning {
|
|
2472
2576
|
/** Whether shape-change physics is enabled. The kit default is token-on. */
|
|
2473
2577
|
shape?: boolean;
|