@pieai/swimmer-ui-kit 1.0.0 → 1.1.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 CHANGED
@@ -3,6 +3,119 @@
3
3
  All notable changes to `@pieai/swimmer-ui-kit`.
4
4
  Format: [Keep a Changelog](https://keepachangelog.com); versioning: semver.
5
5
 
6
+ ## 1.1.0 — 2026-07-13
7
+
8
+ A design-system quality pass: fixes real bugs in the existing surface
9
+ (radial menu, contrast, fonts) and starts paying down the "central kitchen"
10
+ governance/packaging debt (token adoption checker, theme contract export,
11
+ demo CSS split). No exported component or prop was removed or renamed.
12
+
13
+ ### Added
14
+
15
+ - `GAME_UI_THEME_CONTRACT`, an exported `readonly string[]` of every
16
+ semantic token a complete theme (like the official `night` theme) must
17
+ override. Downstream custom themes (e.g. an `abyss` theme) can assert
18
+ their own CSS covers the same list instead of eyeballing the docs.
19
+ - Optional `./fonts.css` subpath: `@font-face` rules + bundled Latin-subset
20
+ woff2 for Baloo 2 and Geist Variable (SIL OFL), the two families
21
+ `theme.css`'s font stack already names. Not loading it still works
22
+ (progressive enhancement, system-font fallback) but non-standard font
23
+ weights (620/860/930) collapse to 400/700 without it.
24
+ - `swimmer-ui-check`, a zero-dependency bin (`npx swimmer-ui-check <dir>`)
25
+ that scans a consumer's own CSS/TSX for raw color literals outside
26
+ `:root`/`[data-*theme*]` blocks — the same "no bare colors in component
27
+ rules" rule `src/tokens.test.ts` enforces inside this repo, made runnable
28
+ against downstream code.
29
+ - `GameModal` gained `position="bottom"`, a mobile action-sheet variant of
30
+ the same native `<dialog>` (unchanged focus trap/Esc/backdrop, only the
31
+ frame's placement/shape/entrance differ). Replaces the hand-rolled
32
+ backdrop+slide-up-panel pattern some consumers were building themselves
33
+ (a known source of missing focus-trap/Esc handling).
34
+ - `GameRadialMenu` gained an `onSelect?: (id: string) => void` callback
35
+ (previously clicking an item did nothing).
36
+ - Demo site and Storybook both gained a light/night theme toggle (demo site:
37
+ navbar control using the kit's own `GameToggle`-style button; Storybook:
38
+ a global toolbar entry that sets `data-game-ui-theme` for every story).
39
+
40
+ ### Changed
41
+
42
+ - **`GameRadialMenu` is now actually styled.** It rendered as unstyled
43
+ browser-default buttons before (`.game-ui-radial-item` had no CSS rule at
44
+ all); it's now a real circular wheel using clock-position placement.
45
+ `role="menu"`/`role="menuitem"` (which had no working keyboard model)
46
+ changed to `role="group"` + plain buttons.
47
+ - **Button/tab/badge text contrast, both themes.** WCAG-measured failures
48
+ as low as 2.52:1 on the primary button in the night theme (need 4.5:1).
49
+ Fixed at the token layer only (`theme.css`): `--game-ui-accent-contrast`
50
+ is now a dark-ink color shared by primary/danger/success buttons, the
51
+ active tab/segmented pill, avatar initials, and the checkbox check —
52
+ component rules did not change. Two further raw-brand-color-as-text
53
+ spots (`--game-ui-danger` on `.game-ui-field-error`/`-required`,
54
+ `--game-ui-accent` on the first-session step numbers) got their own new
55
+ `--game-ui-danger-ink`/`--game-ui-accent-ink` tokens. HUD-chip and
56
+ fact-chip secondary text (`.game-ui-hud-chip small/em`,
57
+ `.game-ui-fact-copy small/em`) stopped fading `--game-ui-text-on-dark` to
58
+ 72% opacity, which dropped under 4.5:1 against realistic glass-chip
59
+ backdrops. All of the above are locked in by an expanded WCAG contrast
60
+ guard test (`src/tokens.test.ts`), not just fixed once.
61
+ - `GameSlider`'s range input now has a token-styled clay thumb/track
62
+ (`::-webkit-slider-thumb`/`::-moz-range-thumb`) instead of bare
63
+ `accent-color`.
64
+ - `GameTooltip` auto-generates `aria-describedby` linking the trigger to
65
+ the tooltip text (was visual-only). `GameTabs` accepts an optional `id` +
66
+ per-tab `panelId` to auto-populate `aria-controls`.
67
+ - Interactive controls get a `@media (forced-colors: active)` fallback
68
+ (Windows High Contrast) — pressed/selected states get a real border
69
+ instead of relying on a `color-mix()` wash forced-colors mode ignores.
70
+ - Storybook's `@storybook/addon-a11y` gate went from cosmetic
71
+ (`test: 'todo'`) to enforced (`test: 'error'`) — a real axe violation now
72
+ fails `pnpm test`, not just shows up in the Storybook UI.
73
+
74
+ ### Fixed
75
+
76
+ - Three accessibility bugs the newly-enforced Storybook a11y gate caught:
77
+ `GameCardFan` used an invalid `role="listitem"` on an `<article>`
78
+ (changed to a plain `<li>`); two scrollable job-list containers in
79
+ `GameContractorTools` had no keyboard focus path; `GameContractorPanel`'s
80
+ mobile drawer rendered the selected job twice (drawer + standalone card)
81
+ when open, producing duplicate ARIA landmarks.
82
+ - `vitest.config.ts`'s unit-test include glob was `src/**/*.test.ts` —
83
+ it silently excluded every `*.test.tsx` file. Five files and ~30 tests
84
+ had never actually run via `pnpm test` despite the command exiting 0.
85
+ Fixed the glob, which then surfaced (and fixed) two further dormant test
86
+ bugs it had been hiding.
87
+ - The demo site's token ledger truncated every value to `var(--…` and
88
+ broke token names mid-word (`TOUCHMINIM UMPX`); values now wrap instead
89
+ of ellipsis-truncating, and cards no longer stretch to their tallest
90
+ sibling in the grid.
91
+
92
+ ### Breaking (packaging only — no component/prop/class/token removed)
93
+
94
+ - **`GameUiPreview`'s demo-only CSS moved to a new optional `./preview.css`
95
+ subpath.** It was ~13KB of stage/showcase-only rules
96
+ (`.game-ui-preview-*`, `.game-ui-swatch`/`.game-ui-token-*`,
97
+ `.game-ui-stage-world`, `.game-ui-proof-frame`, the first-session demo
98
+ world) that every consumer paid for in `styles.css` even though
99
+ `GameUiPreview` is a kit-internal showcase component almost nobody
100
+ renders in production. If you render `<GameUiPreview />` (as TuringPact's
101
+ `/ui-preview` route does), add
102
+ `import '@pieai/swimmer-ui-kit/preview.css'` alongside `./styles.css` or
103
+ it renders unstyled. Everyone else needs no code change —
104
+ `.game-ui-*`/`--game-ui-*` names are unchanged, only which file the CSS
105
+ ships in. `dist/styles.css` dropped from 89.2KB to 77.8KB minified.
106
+
107
+ ## 1.0.1 — 2026-07-11
108
+
109
+ ### Added
110
+
111
+ - `GameCallout`, a token-driven notice surface for wallet pitches, onboarding
112
+ hints, warnings, success messages, and other compact product notices.
113
+
114
+ ### Changed
115
+
116
+ - `GameButton` keeps a 44px minimum hit target on coarse pointers while allowing
117
+ denser 40px controls for precise pointers.
118
+
6
119
  ## 1.0.0 — 2026-07-03
7
120
 
8
121
  1.0 is a stability contract, not a feature drop: exported components and
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+ // Scans consumer CSS for raw color literals living in component rules
3
+ // instead of flowing through SwimmerUIKit tokens. Mirrors the guard check
4
+ // the kit runs on its own styles.css in src/tokens.test.ts, packaged so
5
+ // downstream products can hold their own component CSS to the same
6
+ // "token-only" bar the design-system-guide asks for.
7
+ //
8
+ // Raw colors are expected (and fine) inside token-defining blocks —
9
+ // :root { ... } or an attribute-selector theme block like
10
+ // [data-game-ui-theme='night'] / [data-theme='dark'] — since that is
11
+ // exactly how the design-system-guide tells consumers to re-theme. Only
12
+ // raw colors inside *other* selectors (component rules) are flagged.
13
+ //
14
+ // Usage: swimmer-ui-check [dir] [--ext=css,tsx]
15
+ // dir directory to scan, default "src"
16
+ // --ext comma-separated extensions to scan, default "css"
17
+ import { readFileSync, readdirSync, statSync } from 'node:fs';
18
+ import { extname, join, relative } from 'node:path';
19
+
20
+ const RAW_COLOR = /#[0-9a-fA-F]{3,8}\b|\brgba?\(|\bhsla?\(|\boklch\(/g;
21
+ const TOKEN_BLOCK_SELECTOR = /:root\b|\[data-[\w-]*theme[\w-]*\s*=/i;
22
+ const IGNORE_DIRS = new Set(['node_modules', 'dist', '.git', 'storybook-static', 'site-dist', 'build']);
23
+
24
+ function walk(dir, exts, out) {
25
+ for (const entry of readdirSync(dir)) {
26
+ if (IGNORE_DIRS.has(entry)) continue;
27
+ const full = join(dir, entry);
28
+ const stat = statSync(full);
29
+ if (stat.isDirectory()) walk(full, exts, out);
30
+ else if (exts.has(extname(entry))) out.push(full);
31
+ }
32
+ }
33
+
34
+ function lineFinder(text) {
35
+ const offsets = [0];
36
+ for (let i = 0; i < text.length; i += 1) if (text[i] === '\n') offsets.push(i + 1);
37
+ return (index) => {
38
+ let lo = 0;
39
+ let hi = offsets.length - 1;
40
+ while (lo < hi) {
41
+ const mid = (lo + hi + 1) >> 1;
42
+ if (offsets[mid] <= index) lo = mid;
43
+ else hi = mid - 1;
44
+ }
45
+ return lo + 1;
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Brace-depth scan (not a real CSS parser, good enough for this lint):
51
+ * every `{`/`}` pushes/pops whether the block it opened is a token-defining
52
+ * selector, and raw colors are only reported for declarations whose
53
+ * innermost enclosing block is not one of those.
54
+ */
55
+ function findViolations(css) {
56
+ const withoutComments = css.replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, ' '));
57
+ const lineOf = lineFinder(withoutComments);
58
+
59
+ const blockIsTokenScope = [];
60
+ let blockStart = 0;
61
+ let cursor = 0;
62
+ const violations = [];
63
+
64
+ while (cursor < withoutComments.length) {
65
+ const open = withoutComments.indexOf('{', cursor);
66
+ const close = withoutComments.indexOf('}', cursor);
67
+ if (open === -1 && close === -1) break;
68
+
69
+ if (open !== -1 && (close === -1 || open < close)) {
70
+ const selector = withoutComments.slice(blockStart, open);
71
+ blockIsTokenScope.push(TOKEN_BLOCK_SELECTOR.test(selector));
72
+ blockStart = open + 1;
73
+ cursor = open + 1;
74
+ } else {
75
+ const body = withoutComments.slice(blockStart, close);
76
+ const allowed = blockIsTokenScope.pop() ?? false;
77
+ if (!allowed) {
78
+ for (const match of body.matchAll(RAW_COLOR)) {
79
+ violations.push({ line: lineOf(blockStart + match.index), text: match[0] });
80
+ }
81
+ }
82
+ blockStart = close + 1;
83
+ cursor = close + 1;
84
+ }
85
+ }
86
+ return violations;
87
+ }
88
+
89
+ const args = process.argv.slice(2);
90
+ const target = args.find((a) => !a.startsWith('--')) ?? 'src';
91
+ const extArg = args.find((a) => a.startsWith('--ext='));
92
+ const exts = new Set(
93
+ (extArg ? extArg.slice('--ext='.length).split(',') : ['css']).map((e) => (e.startsWith('.') ? e : `.${e}`)),
94
+ );
95
+
96
+ const files = [];
97
+ try {
98
+ walk(target, exts, files);
99
+ } catch (error) {
100
+ console.error(`swimmer-ui-check: cannot read "${target}": ${error.message}`);
101
+ process.exit(2);
102
+ }
103
+
104
+ let violationCount = 0;
105
+ for (const file of files) {
106
+ const text = readFileSync(file, 'utf8');
107
+ for (const violation of findViolations(text)) {
108
+ console.log(`${relative(process.cwd(), file)}:${violation.line}: raw color literal "${violation.text}" — use var(--game-ui-*) instead`);
109
+ violationCount += 1;
110
+ }
111
+ }
112
+
113
+ if (violationCount > 0) {
114
+ console.error(
115
+ `\nswimmer-ui-check: ${violationCount} raw color literal(s) in ${files.length} file(s) under "${target}". ` +
116
+ 'Raw colors are expected inside :root / [data-*theme*=...] token blocks (that is how you re-theme the kit) ' +
117
+ 'but not inside component rules — see the design-system-guide "主题化配方" section.',
118
+ );
119
+ process.exit(1);
120
+ }
121
+ console.log(`swimmer-ui-check: 0 raw color literals in component rules across ${files.length} file(s) under "${target}". Clean.`);
@@ -0,0 +1,93 @@
1
+ Copyright 2019 The Baloo 2 Project Authors (https://github.com/EkType/Baloo2)
2
+
3
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
4
+ This license is copied below, and is also available with a FAQ at:
5
+ http://scripts.sil.org/OFL
6
+
7
+
8
+ -----------------------------------------------------------
9
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10
+ -----------------------------------------------------------
11
+
12
+ PREAMBLE
13
+ The goals of the Open Font License (OFL) are to stimulate worldwide
14
+ development of collaborative font projects, to support the font creation
15
+ efforts of academic and linguistic communities, and to provide a free and
16
+ open framework in which fonts may be shared and improved in partnership
17
+ with others.
18
+
19
+ The OFL allows the licensed fonts to be used, studied, modified and
20
+ redistributed freely as long as they are not sold by themselves. The
21
+ fonts, including any derivative works, can be bundled, embedded,
22
+ redistributed and/or sold with any software provided that any reserved
23
+ names are not used by derivative works. The fonts and derivatives,
24
+ however, cannot be released under any other type of license. The
25
+ requirement for fonts to remain under this license does not apply
26
+ to any document created using the fonts or their derivatives.
27
+
28
+ DEFINITIONS
29
+ "Font Software" refers to the set of files released by the Copyright
30
+ Holder(s) under this license and clearly marked as such. This may
31
+ include source files, build scripts and documentation.
32
+
33
+ "Reserved Font Name" refers to any names specified as such after the
34
+ copyright statement(s).
35
+
36
+ "Original Version" refers to the collection of Font Software components as
37
+ distributed by the Copyright Holder(s).
38
+
39
+ "Modified Version" refers to any derivative made by adding to, deleting,
40
+ or substituting -- in part or in whole -- any of the components of the
41
+ Original Version, by changing formats or by porting the Font Software to a
42
+ new environment.
43
+
44
+ "Author" refers to any designer, engineer, programmer, technical
45
+ writer or other person who contributed to the Font Software.
46
+
47
+ PERMISSION & CONDITIONS
48
+ Permission is hereby granted, free of charge, to any person obtaining
49
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
50
+ redistribute, and sell modified and unmodified copies of the Font
51
+ Software, subject to the following conditions:
52
+
53
+ 1) Neither the Font Software nor any of its individual components,
54
+ in Original or Modified Versions, may be sold by itself.
55
+
56
+ 2) Original or Modified Versions of the Font Software may be bundled,
57
+ redistributed and/or sold with any software, provided that each copy
58
+ contains the above copyright notice and this license. These can be
59
+ included either as stand-alone text files, human-readable headers or
60
+ in the appropriate machine-readable metadata fields within text or
61
+ binary files as long as those fields can be easily viewed by the user.
62
+
63
+ 3) No Modified Version of the Font Software may use the Reserved Font
64
+ Name(s) unless explicit written permission is granted by the corresponding
65
+ Copyright Holder. This restriction only applies to the primary font name as
66
+ presented to the users.
67
+
68
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69
+ Software shall not be used to promote, endorse or advertise any
70
+ Modified Version, except to acknowledge the contribution(s) of the
71
+ Copyright Holder(s) and the Author(s) or with their explicit written
72
+ permission.
73
+
74
+ 5) The Font Software, modified or unmodified, in part or in whole,
75
+ must be distributed entirely under this license, and must not be
76
+ distributed under any other license. The requirement for fonts to
77
+ remain under this license does not apply to any document created
78
+ using the Font Software.
79
+
80
+ TERMINATION
81
+ This license becomes null and void if any of the above conditions are
82
+ not met.
83
+
84
+ DISCLAIMER
85
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93
+ OTHER DEALINGS IN THE FONT SOFTWARE.
@@ -0,0 +1,92 @@
1
+ Copyright (c) 2023 Vercel, in collaboration with basement.studio
2
+
3
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
4
+ This license is copied below, and is also available with a FAQ at:
5
+ http://scripts.sil.org/OFL
6
+
7
+ -----------------------------------------------------------
8
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
9
+ -----------------------------------------------------------
10
+
11
+ PREAMBLE
12
+ The goals of the Open Font License (OFL) are to stimulate worldwide
13
+ development of collaborative font projects, to support the font creation
14
+ efforts of academic and linguistic communities, and to provide a free and
15
+ open framework in which fonts may be shared and improved in partnership
16
+ with others.
17
+
18
+ The OFL allows the licensed fonts to be used, studied, modified and
19
+ redistributed freely as long as they are not sold by themselves. The
20
+ fonts, including any derivative works, can be bundled, embedded,
21
+ redistributed and/or sold with any software provided that any reserved
22
+ names are not used by derivative works. The fonts and derivatives,
23
+ however, cannot be released under any other type of license. The
24
+ requirement for fonts to remain under this license does not apply
25
+ to any document created using the fonts or their derivatives.
26
+
27
+ DEFINITIONS
28
+ "Font Software" refers to the set of files released by the Copyright
29
+ Holder(s) under this license and clearly marked as such. This may
30
+ include source files, build scripts and documentation.
31
+
32
+ "Reserved Font Name" refers to any names specified as such after the
33
+ copyright statement(s).
34
+
35
+ "Original Version" refers to the collection of Font Software components as
36
+ distributed by the Copyright Holder(s).
37
+
38
+ "Modified Version" refers to any derivative made by adding to, deleting,
39
+ or substituting -- in part or in whole -- any of the components of the
40
+ Original Version, by changing formats or by porting the Font Software to a
41
+ new environment.
42
+
43
+ "Author" refers to any designer, engineer, programmer, technical
44
+ writer or other person who contributed to the Font Software.
45
+
46
+ PERMISSION AND CONDITIONS
47
+ Permission is hereby granted, free of charge, to any person obtaining
48
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
49
+ redistribute, and sell modified and unmodified copies of the Font
50
+ Software, subject to the following conditions:
51
+
52
+ 1) Neither the Font Software nor any of its individual components,
53
+ in Original or Modified Versions, may be sold by itself.
54
+
55
+ 2) Original or Modified Versions of the Font Software may be bundled,
56
+ redistributed and/or sold with any software, provided that each copy
57
+ contains the above copyright notice and this license. These can be
58
+ included either as stand-alone text files, human-readable headers or
59
+ in the appropriate machine-readable metadata fields within text or
60
+ binary files as long as those fields can be easily viewed by the user.
61
+
62
+ 3) No Modified Version of the Font Software may use the Reserved Font
63
+ Name(s) unless explicit written permission is granted by the corresponding
64
+ Copyright Holder. This restriction only applies to the primary font name as
65
+ presented to the users.
66
+
67
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
68
+ Software shall not be used to promote, endorse or advertise any
69
+ Modified Version, except to acknowledge the contribution(s) of the
70
+ Copyright Holder(s) and the Author(s) or with their explicit written
71
+ permission.
72
+
73
+ 5) The Font Software, modified or unmodified, in part or in whole,
74
+ must be distributed entirely under this license, and must not be
75
+ distributed under any other license. The requirement for fonts to
76
+ remain under this license does not apply to any document created
77
+ using the Font Software.
78
+
79
+ TERMINATION
80
+ This license becomes null and void if any of the above conditions are
81
+ not met.
82
+
83
+ DISCLAIMER
84
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
85
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
86
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
87
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
88
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
89
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
90
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
91
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
92
+ OTHER DEALINGS IN THE FONT SOFTWARE.
package/dist/fonts.css ADDED
@@ -0,0 +1 @@
1
+ @font-face{font-family:"Baloo 2";font-style:normal;font-weight:400 800;font-display:swap;src:url(./fonts/baloo-2-latin-variable.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Geist Variable;font-style:normal;font-weight:100 900;font-display:swap;src:url(./fonts/geist-sans-latin-variable.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
package/dist/index.d.ts CHANGED
@@ -55,7 +55,7 @@ export declare const CLAY_ASSETS: {
55
55
 
56
56
  export declare const CLAY_COLOR_TOKENS: {
57
57
  readonly ink: "#3b2d23";
58
- readonly inkMuted: "#7b6652";
58
+ readonly inkMuted: "#786250";
59
59
  readonly parchment: "#fff8ec";
60
60
  readonly parchmentDeep: "#f4e2c6";
61
61
  readonly cream: "#f3e8d8";
@@ -346,7 +346,7 @@ export declare const CLAY_TYPE_TOKENS: {
346
346
  export declare const CLAY_UI_TOKENS: {
347
347
  readonly colors: {
348
348
  readonly ink: "#3b2d23";
349
- readonly inkMuted: "#7b6652";
349
+ readonly inkMuted: "#786250";
350
350
  readonly parchment: "#fff8ec";
351
351
  readonly parchmentDeep: "#f4e2c6";
352
352
  readonly cream: "#f3e8d8";
@@ -593,6 +593,17 @@ export declare const GAME_UI_TARGETS: {
593
593
  readonly mobileLandscapeProofHeightPx: 390;
594
594
  };
595
595
 
596
+ /**
597
+ * The semantic `--game-ui-*` custom properties a *complete* theme must
598
+ * override for full fidelity — this is the same list the official
599
+ * `[data-game-ui-theme='night']` block in theme.css satisfies, enforced by
600
+ * a guard test in src/tokens.test.ts. A downstream product building its own
601
+ * full theme (e.g. `[data-game-ui-theme='abyss']`) can reuse this constant
602
+ * to check its own CSS for the same completeness instead of eyeballing it —
603
+ * see "自定义第三主题" in design-system-guide.md for a usage example.
604
+ */
605
+ export declare const GAME_UI_THEME_CONTRACT: readonly ["--game-ui-bg", "--game-ui-surface", "--game-ui-surface-raised", "--game-ui-panel", "--game-ui-panel-strong", "--game-ui-panel-deep", "--game-ui-text", "--game-ui-text-muted", "--game-ui-accent", "--game-ui-accent-bright", "--game-ui-secondary", "--game-ui-success", "--game-ui-success-bright", "--game-ui-danger", "--game-ui-danger-bright", "--game-ui-warning", "--game-ui-focus-ring", "--game-ui-border-subtle", "--game-ui-border-strong", "--game-ui-disabled", "--game-ui-ink-deep", "--game-ui-border-ink", "--game-ui-wood", "--game-ui-ink-title", "--game-ui-ink-heading"];
606
+
596
607
  export declare const GAME_UI_TOKENS: {
597
608
  readonly iconSm: "var(--game-ui-asset-icon-sm)";
598
609
  readonly iconMd: "var(--game-ui-asset-icon-md)";
@@ -892,6 +903,21 @@ export declare interface GameButtonProps extends ButtonHTMLAttributes<HTMLButton
892
903
 
893
904
  export declare type GameButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger' | 'success';
894
905
 
906
+ /**
907
+ * Compact product notice (wallet pitch, soft warnings, onboarding hints).
908
+ * Token-driven; products may still override cinema theme in host CSS.
909
+ */
910
+ export declare function GameCallout({ heading, children, tone, className, ...props }: GameCalloutProps): ReactNode;
911
+
912
+ export declare interface GameCalloutProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
913
+ /** Visible heading (not HTML title attribute). */
914
+ readonly heading?: ReactNode;
915
+ readonly children: ReactNode;
916
+ readonly tone?: GameCalloutTone;
917
+ }
918
+
919
+ export declare type GameCalloutTone = 'neutral' | 'info' | 'warning' | 'success' | 'danger';
920
+
895
921
  export declare function GameCardFan({ cards, className, label }: GameCardFanProps): ReactNode;
896
922
 
897
923
  export declare interface GameCardFanCard {
@@ -1300,7 +1326,7 @@ export declare interface GameMaterialSwatchesProps {
1300
1326
  'data-testid'?: string | undefined;
1301
1327
  }
1302
1328
 
1303
- export declare function GameModal({ children, className, closeLabel, closeOnBackdrop, footer, onClose, open, size, title, }: GameModalProps): ReactNode;
1329
+ export declare function GameModal({ children, className, closeLabel, closeOnBackdrop, footer, onClose, open, position, size, title, }: GameModalProps): ReactNode;
1304
1330
 
1305
1331
  export declare interface GameModalProps {
1306
1332
  children: ReactNode;
@@ -1312,6 +1338,13 @@ export declare interface GameModalProps {
1312
1338
  /** Called for every close intent: Esc, backdrop click, and close button. */
1313
1339
  onClose: () => void;
1314
1340
  open: boolean;
1341
+ /**
1342
+ * `'bottom'` anchors the frame to the viewport's bottom edge (rounded top
1343
+ * corners only, safe-area-aware padding, slide-up entrance) for mobile
1344
+ * action sheets — same native <dialog> underneath, so focus trap/Esc/
1345
+ * backdrop/top-layer come free either way. Defaults to `'center'`.
1346
+ */
1347
+ position?: 'center' | 'bottom';
1315
1348
  size?: 'sm' | 'md' | 'lg';
1316
1349
  title: string;
1317
1350
  }
@@ -1406,7 +1439,7 @@ export declare interface GamePromptProps {
1406
1439
  title: string;
1407
1440
  }
1408
1441
 
1409
- export declare function GameRadialMenu({ items, label }: GameRadialMenuProps): ReactNode;
1442
+ export declare function GameRadialMenu({ items, label, onSelect }: GameRadialMenuProps): ReactNode;
1410
1443
 
1411
1444
  declare interface GameRadialMenuItem {
1412
1445
  id: string;
@@ -1416,6 +1449,7 @@ declare interface GameRadialMenuItem {
1416
1449
  export declare interface GameRadialMenuProps {
1417
1450
  items: readonly GameRadialMenuItem[];
1418
1451
  label: string;
1452
+ onSelect?: (id: string) => void;
1419
1453
  }
1420
1454
 
1421
1455
  export declare interface GameRobotCrewMember {
@@ -1514,12 +1548,21 @@ export declare type GameSurfaceLayout = 'auto' | 'desktop' | 'mobile';
1514
1548
  export declare interface GameTabItem {
1515
1549
  id: string;
1516
1550
  label: string;
1551
+ /** id of the tabpanel this tab controls; wires aria-controls when set. */
1552
+ panelId?: string;
1517
1553
  }
1518
1554
 
1519
- export declare function GameTabs({ activeId, onSelect, tabs }: GameTabsProps): ReactNode;
1555
+ export declare function GameTabs({ activeId, id, onSelect, tabs }: GameTabsProps): ReactNode;
1520
1556
 
1521
1557
  export declare interface GameTabsProps {
1522
1558
  activeId: string;
1559
+ /**
1560
+ * Base id for this tabs instance (defaults to a generated one). Each tab
1561
+ * button's DOM id is `${id}-${tab.id}` — pass an explicit id so your own
1562
+ * `role="tabpanel"` elements can reference it via aria-labelledby (and set
1563
+ * GameTabItem.panelId so the tab points back via aria-controls).
1564
+ */
1565
+ id?: string;
1523
1566
  onSelect?: (id: string) => void;
1524
1567
  tabs: readonly GameTabItem[];
1525
1568
  }
@@ -1667,6 +1710,7 @@ export declare interface GameToggleProps extends Pick<ButtonHTMLAttributes<HTMLB
1667
1710
  export declare function GameTooltip({ children, label }: GameTooltipProps): ReactNode;
1668
1711
 
1669
1712
  export declare interface GameTooltipProps {
1713
+ /** A single focusable trigger element (e.g. GameIconButton). */
1670
1714
  children: ReactNode;
1671
1715
  label: string;
1672
1716
  }