@freegamestore/games 0.16.0 → 0.19.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 +106 -8
- package/TOKENS.md +132 -0
- package/dist/Confetti.d.ts +18 -0
- package/dist/Confetti.d.ts.map +1 -0
- package/dist/Confetti.js +43 -0
- package/dist/Confetti.js.map +1 -0
- package/dist/GameAuth.d.ts.map +1 -1
- package/dist/GameAuth.js +5 -87
- package/dist/GameAuth.js.map +1 -1
- package/dist/GameButton.d.ts +2 -1
- package/dist/GameButton.d.ts.map +1 -1
- package/dist/GameButton.js +16 -67
- package/dist/GameButton.js.map +1 -1
- package/dist/GameConfirm.d.ts +1 -1
- package/dist/GameConfirm.d.ts.map +1 -1
- package/dist/GameConfirm.js +1 -7
- package/dist/GameConfirm.js.map +1 -1
- package/dist/GameModal.d.ts +3 -2
- package/dist/GameModal.d.ts.map +1 -1
- package/dist/GameModal.js +3 -40
- package/dist/GameModal.js.map +1 -1
- package/dist/GameOverScreen.d.ts +3 -2
- package/dist/GameOverScreen.d.ts.map +1 -1
- package/dist/GameOverScreen.js +3 -37
- package/dist/GameOverScreen.js.map +1 -1
- package/dist/GameResultScreen.d.ts +41 -0
- package/dist/GameResultScreen.d.ts.map +1 -0
- package/dist/GameResultScreen.js +25 -0
- package/dist/GameResultScreen.js.map +1 -0
- package/dist/GameShell.d.ts +3 -0
- package/dist/GameShell.d.ts.map +1 -1
- package/dist/GameShell.js +4 -43
- package/dist/GameShell.js.map +1 -1
- package/dist/GameTextSizeToggle.d.ts +1 -1
- package/dist/GameTextSizeToggle.d.ts.map +1 -1
- package/dist/GameTextSizeToggle.js +1 -16
- package/dist/GameTextSizeToggle.js.map +1 -1
- package/dist/GameThemeToggle.d.ts +1 -1
- package/dist/GameThemeToggle.d.ts.map +1 -1
- package/dist/GameThemeToggle.js +44 -21
- package/dist/GameThemeToggle.js.map +1 -1
- package/dist/GameTopbar.d.ts +12 -7
- package/dist/GameTopbar.d.ts.map +1 -1
- package/dist/GameTopbar.js +54 -188
- package/dist/GameTopbar.js.map +1 -1
- package/dist/GameTopbar.module.css +201 -0
- package/dist/Leaderboard.d.ts.map +1 -1
- package/dist/Leaderboard.js +2 -5
- package/dist/Leaderboard.js.map +1 -1
- package/dist/StartScreen.d.ts +41 -0
- package/dist/StartScreen.d.ts.map +1 -0
- package/dist/StartScreen.js +30 -0
- package/dist/StartScreen.js.map +1 -0
- package/dist/feel/ScorePopup.d.ts +20 -0
- package/dist/feel/ScorePopup.d.ts.map +1 -0
- package/dist/feel/ScorePopup.js +51 -0
- package/dist/feel/ScorePopup.js.map +1 -0
- package/dist/feel/easing.d.ts +14 -0
- package/dist/feel/easing.d.ts.map +1 -0
- package/dist/feel/easing.js +12 -0
- package/dist/feel/easing.js.map +1 -0
- package/dist/feel/haptics.d.ts +19 -0
- package/dist/feel/haptics.d.ts.map +1 -0
- package/dist/feel/haptics.js +24 -0
- package/dist/feel/haptics.js.map +1 -0
- package/dist/feel/index.d.ts +17 -0
- package/dist/feel/index.d.ts.map +1 -0
- package/dist/feel/index.js +17 -0
- package/dist/feel/index.js.map +1 -0
- package/dist/feel/particles.d.ts +46 -0
- package/dist/feel/particles.d.ts.map +1 -0
- package/dist/feel/particles.js +61 -0
- package/dist/feel/particles.js.map +1 -0
- package/dist/feel/reducedMotion.d.ts +6 -0
- package/dist/feel/reducedMotion.d.ts.map +1 -0
- package/dist/feel/reducedMotion.js +10 -0
- package/dist/feel/reducedMotion.js.map +1 -0
- package/dist/feel/shake.d.ts +6 -0
- package/dist/feel/shake.d.ts.map +1 -0
- package/dist/feel/shake.js +17 -0
- package/dist/feel/shake.js.map +1 -0
- package/dist/feel/tween.d.ts +9 -0
- package/dist/feel/tween.d.ts.map +1 -0
- package/dist/feel/tween.js +15 -0
- package/dist/feel/tween.js.map +1 -0
- package/dist/feel/useHaptics.d.ts +12 -0
- package/dist/feel/useHaptics.d.ts.map +1 -0
- package/dist/feel/useHaptics.js +16 -0
- package/dist/feel/useHaptics.js.map +1 -0
- package/dist/feel/useParticles.d.ts +18 -0
- package/dist/feel/useParticles.d.ts.map +1 -0
- package/dist/feel/useParticles.js +79 -0
- package/dist/feel/useParticles.js.map +1 -0
- package/dist/feel/useShake.d.ts +14 -0
- package/dist/feel/useShake.d.ts.map +1 -0
- package/dist/feel/useShake.js +17 -0
- package/dist/feel/useShake.js.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -2
- package/dist/index.js.map +1 -1
- package/dist/theme.css +93 -0
- package/dist/tokens.d.ts +112 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +106 -0
- package/dist/tokens.js.map +1 -0
- package/dist/useGameSounds.d.ts.map +1 -1
- package/dist/useGameSounds.js +37 -15
- package/dist/useGameSounds.js.map +1 -1
- package/dist/useLeaderboard.d.ts +8 -1
- package/dist/useLeaderboard.d.ts.map +1 -1
- package/dist/useLeaderboard.js +21 -11
- package/dist/useLeaderboard.js.map +1 -1
- package/package.json +23 -7
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Shared React UI primitives for games on **freegamestore.online**. Brand-consistent layout, scroll-free viewport lock, touch-friendly controls, and synthesized sound effects.
|
|
4
4
|
|
|
5
|
-
**Version:** 0.
|
|
5
|
+
**Version:** 0.19.0
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -14,6 +14,50 @@ pnpm add @freegamestore/games
|
|
|
14
14
|
|
|
15
15
|
Peer dependency: React 19+.
|
|
16
16
|
|
|
17
|
+
## Styling (Tailwind 4 on tokens)
|
|
18
|
+
|
|
19
|
+
The components are styled with **Tailwind utilities on the platform tokens**
|
|
20
|
+
([ADR-001](https://github.com/freegamestore-online/platform/blob/main/docs/adr/001-tailwind-on-fgs-tokens.md)).
|
|
21
|
+
The package ships class names, not CSS, so **your game compiles them**. In the
|
|
22
|
+
game's `web/src/index.css`, directly after `@import "tailwindcss"`:
|
|
23
|
+
|
|
24
|
+
```css
|
|
25
|
+
@import "tailwindcss";
|
|
26
|
+
@import "@freegamestore/games/theme.css";
|
|
27
|
+
@source "../node_modules/@freegamestore/games/dist";
|
|
28
|
+
|
|
29
|
+
@theme inline {
|
|
30
|
+
--color-paper: var(--paper);
|
|
31
|
+
--color-panel: var(--panel);
|
|
32
|
+
--color-ink: var(--ink);
|
|
33
|
+
--color-muted: var(--muted);
|
|
34
|
+
--color-line: var(--line);
|
|
35
|
+
--color-accent: var(--accent);
|
|
36
|
+
--color-danger: var(--danger);
|
|
37
|
+
--font-display: "Fraunces", serif;
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- `@source` makes Tailwind scan this package's `dist` for the classes the
|
|
42
|
+
components use. Without it the SDK renders unstyled.
|
|
43
|
+
- `theme.css` adds what the components need beyond the token mapping: the
|
|
44
|
+
`animate-fgs-*` animations and their keyframes (it replaces the CSS the
|
|
45
|
+
components used to inject), `accent-ink`, `accent-soft` and the `fgs` radius
|
|
46
|
+
and text sizes. Durations and easings read the `--motion-*` tokens.
|
|
47
|
+
- The `@theme inline` block is the token mapping every `template-game-*` carries.
|
|
48
|
+
A game on the templates already has it.
|
|
49
|
+
- Floating UI (`GameModal`, `GameConfirm`, `StartScreen`, `GameResultScreen`)
|
|
50
|
+
sits on the opaque `bg-panel` surface. Only the scrim behind it is translucent.
|
|
51
|
+
- Nothing is injected at runtime. The `*_CSS` strings (`CONFETTI_CSS`,
|
|
52
|
+
`SCORE_POP_CSS`, `RESULT_SCREEN_CSS`, `START_SCREEN_CSS`) are removed in 0.19.0;
|
|
53
|
+
`GAME_TOKENS_CSS` stays as the documented token helper.
|
|
54
|
+
- Inline `style` remains only for values computed at runtime (confetti piece
|
|
55
|
+
positions, score-popup coordinates, stagger indexes), each marked
|
|
56
|
+
`// Dynamic: …`. `src/styling.test.ts` fails on anything else.
|
|
57
|
+
- Hooks for tests and overrides are `data-fgs="…"` attributes
|
|
58
|
+
(`result`, `confetti`, `start`, `start-artwork`, `score-popup`). The old
|
|
59
|
+
`fgs-*` class names are gone.
|
|
60
|
+
|
|
17
61
|
## Quick Start
|
|
18
62
|
|
|
19
63
|
```tsx
|
|
@@ -21,7 +65,7 @@ import { GameShell, GameTopbar } from '@freegamestore/games'
|
|
|
21
65
|
|
|
22
66
|
export default function App() {
|
|
23
67
|
return (
|
|
24
|
-
<GameShell topbar={<GameTopbar title="Chess"
|
|
68
|
+
<GameShell topbar={<GameTopbar title="Chess" />}>
|
|
25
69
|
{/* your game canvas / DOM */}
|
|
26
70
|
</GameShell>
|
|
27
71
|
)
|
|
@@ -35,7 +79,7 @@ export default function App() {
|
|
|
35
79
|
Root layout wrapper. Locks the game to `100svh`, prevents document scroll, disables text selection and touch callout. Every game wraps its content in this.
|
|
36
80
|
|
|
37
81
|
```tsx
|
|
38
|
-
<GameShell topbar={<GameTopbar title="Tetris"
|
|
82
|
+
<GameShell topbar={<GameTopbar title="Tetris" />}>
|
|
39
83
|
<MyGame />
|
|
40
84
|
</GameShell>
|
|
41
85
|
```
|
|
@@ -50,8 +94,11 @@ Root layout wrapper. Locks the game to `100svh`, prevents document scroll, disab
|
|
|
50
94
|
The single allowed topbar. Same font, padding, and color tokens across every game.
|
|
51
95
|
|
|
52
96
|
```tsx
|
|
53
|
-
// Simple: just
|
|
54
|
-
<GameTopbar title="Tetris"
|
|
97
|
+
// Simple: just the title. The score pill is hidden by default (#348)
|
|
98
|
+
<GameTopbar title="Tetris" />
|
|
99
|
+
|
|
100
|
+
// Opt in to the live score pill
|
|
101
|
+
<GameTopbar title="Tetris" score={42} showScore />
|
|
55
102
|
|
|
56
103
|
// Custom stats + play/pause controls
|
|
57
104
|
<GameTopbar
|
|
@@ -71,8 +118,9 @@ The single allowed topbar. Same font, padding, and color tokens across every gam
|
|
|
71
118
|
| Prop | Type | Description |
|
|
72
119
|
|------|------|-------------|
|
|
73
120
|
| `title` | `string?` | Game name, left side |
|
|
74
|
-
| `score` | `number?` |
|
|
75
|
-
| `
|
|
121
|
+
| `score` | `number?` | Live score, shown only when `showScore` is set |
|
|
122
|
+
| `showScore` | `boolean?` | Show the live score pill. Off by default (#348) |
|
|
123
|
+
| `stats` | `GameTopbarStat[]?` | Custom stat lineup (lives, level, time) |
|
|
76
124
|
| `actions` | `ReactNode?` | Right-side buttons (max 2) |
|
|
77
125
|
| `rules` | `ReactNode?` | Game instructions — shows info icon, opens overlay on tap |
|
|
78
126
|
| `onPlayPause` | `() => void?` | Renders play/pause icon button |
|
|
@@ -84,7 +132,7 @@ The single allowed topbar. Same font, padding, and color tokens across every gam
|
|
|
84
132
|
Sign-in / avatar widget for the topbar `actions` slot. Shows "Sign in" when signed out, avatar + dropdown when signed in.
|
|
85
133
|
|
|
86
134
|
```tsx
|
|
87
|
-
<GameTopbar title="Chess"
|
|
135
|
+
<GameTopbar title="Chess" actions={<GameAuth />} />
|
|
88
136
|
```
|
|
89
137
|
|
|
90
138
|
No props — uses `useAuth` internally.
|
|
@@ -149,6 +197,49 @@ Standard game-over overlay with score, high score, and play again button.
|
|
|
149
197
|
| `onPlayAgain` | `() => void` | Play again handler |
|
|
150
198
|
| `children` | `ReactNode?` | Extra content below the score |
|
|
151
199
|
|
|
200
|
+
|
|
201
|
+
### GameResultScreen
|
|
202
|
+
|
|
203
|
+
An animated result screen for a finished level or game. A win shows falling confetti and an accent title. A loss shows the card alone. The action is focused when the screen opens. Use `GameOverScreen` for a plain game-over card. `GameResultScreen` is the animated version.
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
<GameResultScreen
|
|
207
|
+
tone={won ? 'win' : 'lose'}
|
|
208
|
+
eyebrow="Level 4"
|
|
209
|
+
title={won ? 'Level 4 cleared!' : 'Game over'}
|
|
210
|
+
message={won ? undefined : 'Your tray filled up.'}
|
|
211
|
+
stats={[
|
|
212
|
+
{ label: 'Score', value: score.toLocaleString() },
|
|
213
|
+
{ label: 'Moves', value: moves },
|
|
214
|
+
]}
|
|
215
|
+
badge={newBest ? 'New best!' : undefined}
|
|
216
|
+
action={won ? { label: 'Next level', onClick: next } : { label: 'Play again', onClick: restart }}
|
|
217
|
+
>
|
|
218
|
+
<Leaderboard {...leaderboard} />
|
|
219
|
+
</GameResultScreen>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
| Prop | Type | Description |
|
|
223
|
+
|------|------|-------------|
|
|
224
|
+
| `tone` | `'win' \| 'lose'` | `win` shows confetti and the accent title colour |
|
|
225
|
+
| `eyebrow` | `string?` | Small line above the title, such as the level name |
|
|
226
|
+
| `title` | `string` | The headline |
|
|
227
|
+
| `message` | `string?` | One line under the title |
|
|
228
|
+
| `stats` | `{ label, value }[]?` | Stat rows in a two-column grid. `value` may be a number |
|
|
229
|
+
| `badge` | `ReactNode?` | Highlight line under the stats, such as "New best!" |
|
|
230
|
+
| `action` | `{ label, onClick }` | The primary button. It is focused when the screen opens |
|
|
231
|
+
| `children` | `ReactNode?` | Extra content between the badge and the action |
|
|
232
|
+
|
|
233
|
+
The screen covers the viewport and has `role="dialog"`. Motion is the
|
|
234
|
+
`animate-fgs-*` utilities from `theme.css` (see [Styling](#styling-tailwind-4-on-tokens)),
|
|
235
|
+
which stop under `prefers-reduced-motion`, and the confetti is hidden for players
|
|
236
|
+
who ask for less motion. Its colours come from the platform tokens and its
|
|
237
|
+
durations from the `--motion-*` tokens in [TOKENS.md](./TOKENS.md), with the
|
|
238
|
+
light-theme fallbacks in `theme.css`.
|
|
239
|
+
|
|
240
|
+
`Confetti` is exported on its own, for a game that wants the celebration
|
|
241
|
+
somewhere else.
|
|
242
|
+
|
|
152
243
|
### GameConfirm
|
|
153
244
|
|
|
154
245
|
Confirm/cancel dialog built on GameModal.
|
|
@@ -234,6 +325,13 @@ const { topScores, recentScores, submitScore, loading, refresh } = useLeaderboar
|
|
|
234
325
|
const { ok, rank } = await submitScore(1200)
|
|
235
326
|
```
|
|
236
327
|
|
|
328
|
+
Pass `period` (a UTC day, `YYYY-MM-DD`) for a per-day board, such as a daily puzzle. Omit it for the all-time board:
|
|
329
|
+
|
|
330
|
+
```tsx
|
|
331
|
+
const today = new Date().toISOString().slice(0, 10)
|
|
332
|
+
const { topScores, submitScore } = useLeaderboard('tile-match', { period: today })
|
|
333
|
+
```
|
|
334
|
+
|
|
237
335
|
### useGameSounds
|
|
238
336
|
|
|
239
337
|
Synthesized sound effects via Web Audio API. Zero audio files. All sounds auto-respect the mute toggle.
|
package/TOKENS.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Design tokens
|
|
2
|
+
|
|
3
|
+
`@freegamestore/games` defines the brand, typography, shape and motion tokens
|
|
4
|
+
that every FreeGameStore game uses. They are lifted from the Mosaic Rush pilot,
|
|
5
|
+
which is the first game built on them. The source is `src/tokens.ts`. This file
|
|
6
|
+
is the reference.
|
|
7
|
+
|
|
8
|
+
## Where the values live
|
|
9
|
+
|
|
10
|
+
| Layer | Where | What it does |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| Definitions | `src/tokens.ts` | Exports the values as TypeScript constants and builds `GAME_TOKENS_CSS`. |
|
|
13
|
+
| Consumption | SDK components | Read `var(--paper)` and the other tokens inline, with a fallback for each. |
|
|
14
|
+
| Injection | Each game | Adds `GAME_TOKENS_CSS` to its stylesheet. Nothing is injected on import, because the package is `sideEffects: false`. |
|
|
15
|
+
|
|
16
|
+
Before this package defined them, the brand colours (`--paper`, `--ink`,
|
|
17
|
+
`--accent`, `--panel`, `--line`, `--muted`) were defined only in each game's own
|
|
18
|
+
`index.css`. Components in the SDK used them with fallbacks, so a game that
|
|
19
|
+
did not define them still rendered. Defining them here gives every game the
|
|
20
|
+
same values.
|
|
21
|
+
|
|
22
|
+
## Using the tokens
|
|
23
|
+
|
|
24
|
+
Add the stylesheet once, at the top of the game:
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import { GAME_TOKENS_CSS } from '@freegamestore/games';
|
|
28
|
+
|
|
29
|
+
// In the root component:
|
|
30
|
+
<style>{GAME_TOKENS_CSS}</style>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Or copy `GAME_TOKENS_CSS` into `index.css`, which is what Mosaic Rush does.
|
|
34
|
+
Then use the tokens in your own CSS:
|
|
35
|
+
|
|
36
|
+
```css
|
|
37
|
+
.card {
|
|
38
|
+
background: var(--panel);
|
|
39
|
+
border: 1px solid var(--line);
|
|
40
|
+
border-radius: var(--radius);
|
|
41
|
+
font-family: var(--font-display);
|
|
42
|
+
transition: transform var(--motion-fast) var(--motion-ease-out);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The dark theme applies under `data-theme="dark"` on `<html>`.
|
|
47
|
+
`GameThemeToggle` sets that attribute. The theme follows the system preference
|
|
48
|
+
until the player picks one.
|
|
49
|
+
|
|
50
|
+
## Colours
|
|
51
|
+
|
|
52
|
+
Light values are the defaults on `:root`. The dark values override only the
|
|
53
|
+
tokens that differ, under `:root[data-theme="dark"]`.
|
|
54
|
+
|
|
55
|
+
| Token | Light | Dark | Use |
|
|
56
|
+
|---|---|---|---|
|
|
57
|
+
| `--paper` | `#ffffff` | `#0f0f0f` | Page and board background. |
|
|
58
|
+
| `--ink` | `#1a1a1a` | `#f5f5f5` | Primary text. |
|
|
59
|
+
| `--muted` | `#6b7280` | `#9ca3af` | Secondary text and captions. |
|
|
60
|
+
| `--line` | `#e5e7eb` | `#2d2d2d` | Hairline borders on empty surfaces. |
|
|
61
|
+
| `--line-strong` | `#d1d5db` | `#404040` | Borders on buttons and toggles. |
|
|
62
|
+
| `--panel` | `#f9fafb` | `#1a1a1a` | Raised cards and overlays. |
|
|
63
|
+
| `--glass` | `rgba(255, 255, 255, 0.8)` | `rgba(15, 15, 15, 0.8)` | Frosted overlay surfaces. |
|
|
64
|
+
| `--dock` | `#ffffff` | `#1a1a1a` | Bottom dock. |
|
|
65
|
+
| `--success` | `#16a34a` | `#22c55e` | Success status. |
|
|
66
|
+
| `--warning` | `#d97706` | `#fbbf24` | Warning status. |
|
|
67
|
+
| `--error` | `#dc2626` | `#ef4444` | Error status. |
|
|
68
|
+
| `--accent` | `#2563eb` | `#2563eb` | Primary actions and highlights. The same in both themes. |
|
|
69
|
+
| `--accent-soft` | `#eff6ff` | `#1a2e26` | Quiet fill behind accent text, such as badges. |
|
|
70
|
+
| `--accent-ink` | `#2563eb` | `#60a5fa` | Accent used as text. Lighter in dark mode so it passes AA. |
|
|
71
|
+
|
|
72
|
+
Ink, muted and accent-ink each meet WCAG AA (4.5:1) on `--paper` in both themes.
|
|
73
|
+
`src/tokens.test.ts` checks this.
|
|
74
|
+
|
|
75
|
+
Use `--accent` for fills, such as buttons, and `--accent-ink` for accent text.
|
|
76
|
+
`--accent` on a dark surface does not pass AA as text.
|
|
77
|
+
|
|
78
|
+
## Typography
|
|
79
|
+
|
|
80
|
+
Only display headings and score values use the display face. Body text keeps the
|
|
81
|
+
platform's default sizes.
|
|
82
|
+
|
|
83
|
+
| Token | Value | Use |
|
|
84
|
+
|---|---|---|
|
|
85
|
+
| `--font-display` | `"Fraunces", Georgia, serif` | Titles and score values. |
|
|
86
|
+
| `--font-body` | `"Manrope", system-ui, sans-serif` | Everything else. |
|
|
87
|
+
| `--text-stat` | `1.25rem` | Score and stat values. |
|
|
88
|
+
| `--text-title` | `clamp(2rem, 9vw, 2.6rem)` | Overlay titles. |
|
|
89
|
+
| `--text-display` | `clamp(2.25rem, 11vw, 3.25rem)` | The start-screen title. |
|
|
90
|
+
|
|
91
|
+
The brand fonts (Manrope and Fraunces) are the only ones allowed. The compliance
|
|
92
|
+
suite checks for them.
|
|
93
|
+
|
|
94
|
+
## Shape
|
|
95
|
+
|
|
96
|
+
| Token | Value | Use |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| `--radius` | `1.25rem` | Corner radius for cards and overlays. |
|
|
99
|
+
|
|
100
|
+
## Motion
|
|
101
|
+
|
|
102
|
+
Every animation reads its duration and easing from these tokens, so one change
|
|
103
|
+
affects the whole game.
|
|
104
|
+
|
|
105
|
+
| Token | Value | Use |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| `--motion-fast` | `120ms` | Presses and pops. |
|
|
108
|
+
| `--motion-base` | `240ms` | Standard transitions. |
|
|
109
|
+
| `--motion-slow` | `420ms` | Larger entrances and overlays. |
|
|
110
|
+
| `--motion-ease-out` | `cubic-bezier(0.2, 0.9, 0.3, 1)` | Default ease-out. |
|
|
111
|
+
| `--motion-ease-spring` | `cubic-bezier(0.2, 0.9, 0.3, 1.3)` | Overshooting pops. |
|
|
112
|
+
| `--motion-duration` | `var(--motion-fast)`, so `120ms` | Default duration for a control transition, such as a press. Reduced motion sets it to `0ms`. |
|
|
113
|
+
| `--motion-easing` | `var(--motion-ease-out)` | Default easing for a control transition. |
|
|
114
|
+
|
|
115
|
+
### Reduced motion
|
|
116
|
+
|
|
117
|
+
Under `prefers-reduced-motion: reduce`, the three durations are set to `0ms`.
|
|
118
|
+
Animations finish at once and only the final state shows. The easings do not
|
|
119
|
+
change.
|
|
120
|
+
|
|
121
|
+
Animations that depend on a duration then finish instantly. Any animation that
|
|
122
|
+
is pure decoration should also be disabled explicitly, as Mosaic Rush does for
|
|
123
|
+
its confetti and float effects.
|
|
124
|
+
|
|
125
|
+
## Compatibility
|
|
126
|
+
|
|
127
|
+
- Adding these tokens changes no existing export. The SDK components keep their
|
|
128
|
+
fallbacks.
|
|
129
|
+
- Games that already define `--paper` and the others in their own `index.css`
|
|
130
|
+
keep working as long as their own stylesheet loads after the tokens, so its declarations win.
|
|
131
|
+
- The store-site aliases from Mosaic Rush (`--bg`, `--surface`, `--border`) are
|
|
132
|
+
not part of this package.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type * as React from 'react';
|
|
2
|
+
/** The default palette: the four status-and-accent colours, following the theme. */
|
|
3
|
+
export declare const CONFETTI_COLOURS: readonly string[];
|
|
4
|
+
export interface ConfettiProps {
|
|
5
|
+
/** Colours cycled across the pieces. Default: the accent and the status colours. */
|
|
6
|
+
colours?: readonly string[];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Falling confetti for a win. Each piece's position, drift, spin and timing
|
|
10
|
+
* come from its index, so the render is the same every time. Renders nothing
|
|
11
|
+
* under prefers-reduced-motion.
|
|
12
|
+
*
|
|
13
|
+
* The falling motion is the `animate-fgs-confetti` utility from the SDK's
|
|
14
|
+
* `theme.css`. Only the per-piece values below are inline, because they are
|
|
15
|
+
* computed from the piece's index.
|
|
16
|
+
*/
|
|
17
|
+
export declare function Confetti({ colours }: ConfettiProps): React.JSX.Element | null;
|
|
18
|
+
//# sourceMappingURL=Confetti.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Confetti.d.ts","sourceRoot":"","sources":["../src/Confetti.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AASpC,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAK7C,CAAC;AASF,MAAM,WAAW,aAAa;IAC5B,oFAAoF;IACpF,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,EAAE,OAA0B,EAAE,EAAE,aAAa,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CA4BhG"}
|
package/dist/Confetti.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { LIGHT_COLOURS } from './tokens.js';
|
|
3
|
+
/** Number of falling pieces. */
|
|
4
|
+
const PIECES = 30;
|
|
5
|
+
/** A CSS variable with a fallback taken from the #209 light tokens. */
|
|
6
|
+
const v = (name, fallback) => `var(--${name}, ${fallback})`;
|
|
7
|
+
/** The default palette: the four status-and-accent colours, following the theme. */
|
|
8
|
+
export const CONFETTI_COLOURS = [
|
|
9
|
+
v('accent', LIGHT_COLOURS.accent),
|
|
10
|
+
v('success', LIGHT_COLOURS.success),
|
|
11
|
+
v('warning', LIGHT_COLOURS.warning),
|
|
12
|
+
v('danger', LIGHT_COLOURS.error),
|
|
13
|
+
];
|
|
14
|
+
/** True when the player has asked for less motion. False on the server, where there is no window. */
|
|
15
|
+
function prefersReducedMotion() {
|
|
16
|
+
return typeof window !== 'undefined' && typeof window.matchMedia === 'function'
|
|
17
|
+
? window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
18
|
+
: false;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Falling confetti for a win. Each piece's position, drift, spin and timing
|
|
22
|
+
* come from its index, so the render is the same every time. Renders nothing
|
|
23
|
+
* under prefers-reduced-motion.
|
|
24
|
+
*
|
|
25
|
+
* The falling motion is the `animate-fgs-confetti` utility from the SDK's
|
|
26
|
+
* `theme.css`. Only the per-piece values below are inline, because they are
|
|
27
|
+
* computed from the piece's index.
|
|
28
|
+
*/
|
|
29
|
+
export function Confetti({ colours = CONFETTI_COLOURS }) {
|
|
30
|
+
if (prefersReducedMotion())
|
|
31
|
+
return null;
|
|
32
|
+
return (_jsx("div", { "data-fgs": "confetti", className: "pointer-events-none absolute inset-0 z-0 overflow-hidden motion-reduce:hidden", "aria-hidden": "true", children: Array.from({ length: PIECES }, (_, i) => (_jsx("span", { "data-fgs": "confetti-piece", className: "absolute -top-3 left-(--x) h-[0.8rem] w-2 animate-fgs-confetti rounded-[2px] bg-(--colour) nth-[3n]:size-[0.6rem] nth-[3n]:rounded-full",
|
|
33
|
+
// Dynamic: position, drift, spin, timing and colour all come from the piece index.
|
|
34
|
+
style: {
|
|
35
|
+
'--x': `${(i * 37) % 100}%`,
|
|
36
|
+
'--drift': `${((i * 53) % 41) - 20}px`,
|
|
37
|
+
'--spin': `${((i * 97) % 720) - 360}deg`,
|
|
38
|
+
'--delay': `${(i % 7) * 70}ms`,
|
|
39
|
+
'--duration': `${1500 + (i % 5) * 200}ms`,
|
|
40
|
+
'--colour': colours[i % colours.length],
|
|
41
|
+
} }, i))) }));
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=Confetti.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Confetti.js","sourceRoot":"","sources":["../src/Confetti.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,gCAAgC;AAChC,MAAM,MAAM,GAAG,EAAE,CAAC;AAElB,uEAAuE;AACvE,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,QAAgB,EAAE,EAAE,CAAC,SAAS,IAAI,KAAK,QAAQ,GAAG,CAAC;AAE5E,oFAAoF;AACpF,MAAM,CAAC,MAAM,gBAAgB,GAAsB;IACjD,CAAC,CAAC,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC;IACjC,CAAC,CAAC,SAAS,EAAE,aAAa,CAAC,OAAO,CAAC;IACnC,CAAC,CAAC,SAAS,EAAE,aAAa,CAAC,OAAO,CAAC;IACnC,CAAC,CAAC,QAAQ,EAAE,aAAa,CAAC,KAAK,CAAC;CACjC,CAAC;AAEF,qGAAqG;AACrG,SAAS,oBAAoB;IAC3B,OAAO,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,UAAU;QAC7E,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,kCAAkC,CAAC,CAAC,OAAO;QAC/D,CAAC,CAAC,KAAK,CAAC;AACZ,CAAC;AAOD;;;;;;;;GAQG;AACH,MAAM,UAAU,QAAQ,CAAC,EAAE,OAAO,GAAG,gBAAgB,EAAiB;IACpE,IAAI,oBAAoB,EAAE;QAAE,OAAO,IAAI,CAAC;IACxC,OAAO,CACL,0BACW,UAAU,EACnB,SAAS,EAAC,+EAA+E,iBAC7E,MAAM,YAEjB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CACxC,2BAEW,gBAAgB,EACzB,SAAS,EAAC,yIAAyI;YACnJ,mFAAmF;YACnF,KAAK,EACH;gBACE,KAAK,EAAE,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,GAAG,GAAG;gBAC3B,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,IAAI;gBACtC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,KAAK;gBACxC,SAAS,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI;gBAC9B,YAAY,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,IAAI;gBACzC,UAAU,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;aACjB,IAZrB,CAAC,CAcN,CACH,CAAC,GACE,CACP,CAAC;AACJ,CAAC"}
|
package/dist/GameAuth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GameAuth.d.ts","sourceRoot":"","sources":["../src/GameAuth.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"GameAuth.d.ts","sourceRoot":"","sources":["../src/GameAuth.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAQpC;;;;;;GAMG;AACH,wBAAgB,QAAQ,IAAI,KAAK,CAAC,GAAG,CAAC,OAAO,CAsE5C"}
|
package/dist/GameAuth.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from 'react';
|
|
3
3
|
import { useAuth } from './useAuth.js';
|
|
4
|
+
/** Shared by the sign-in and avatar buttons: bare, 44px minimum touch target. */
|
|
5
|
+
const BARE_BUTTON = 'm-0 inline-flex min-h-11 min-w-11 cursor-pointer touch-manipulation items-center justify-center border-0 bg-transparent p-0 [-webkit-tap-highlight-color:transparent]';
|
|
4
6
|
/**
|
|
5
7
|
* Sign-in / avatar widget for the GameTopbar `actions` slot.
|
|
6
8
|
*
|
|
@@ -27,100 +29,16 @@ export function GameAuth() {
|
|
|
27
29
|
if (loading)
|
|
28
30
|
return _jsx("div", {});
|
|
29
31
|
if (user === null) {
|
|
30
|
-
return (_jsx("button", { onClick: signIn,
|
|
31
|
-
background: 'none',
|
|
32
|
-
border: 'none',
|
|
33
|
-
cursor: 'pointer',
|
|
34
|
-
fontFamily: '"Manrope", system-ui, sans-serif',
|
|
35
|
-
fontSize: '0.7rem',
|
|
36
|
-
fontWeight: 600,
|
|
37
|
-
color: 'var(--muted, #6b7280)',
|
|
38
|
-
padding: 0,
|
|
39
|
-
margin: 0,
|
|
40
|
-
// 44px minimum touch target
|
|
41
|
-
minWidth: '44px',
|
|
42
|
-
minHeight: '44px',
|
|
43
|
-
display: 'inline-flex',
|
|
44
|
-
alignItems: 'center',
|
|
45
|
-
justifyContent: 'center',
|
|
46
|
-
WebkitTapHighlightColor: 'transparent',
|
|
47
|
-
touchAction: 'manipulation',
|
|
48
|
-
}, children: "Sign in" }));
|
|
32
|
+
return (_jsx("button", { onClick: signIn, className: `${BARE_BUTTON} text-[0.7rem] font-semibold text-muted`, children: "Sign in" }));
|
|
49
33
|
}
|
|
50
34
|
const firstName = user.name.split(' ')[0] ?? user.name;
|
|
51
35
|
// Google accounts without a profile picture have an empty avatar URL;
|
|
52
36
|
// render an initial-letter circle instead of a broken <img>.
|
|
53
37
|
const avatarUrl = user.avatar?.trim();
|
|
54
38
|
const initial = (firstName.charAt(0) || '?').toUpperCase();
|
|
55
|
-
return (_jsxs("div", { ref: ref,
|
|
56
|
-
background: 'none',
|
|
57
|
-
border: 'none',
|
|
58
|
-
cursor: 'pointer',
|
|
59
|
-
display: 'inline-flex',
|
|
60
|
-
alignItems: 'center',
|
|
61
|
-
gap: '0.35rem',
|
|
62
|
-
padding: 0,
|
|
63
|
-
margin: 0,
|
|
64
|
-
minWidth: '44px',
|
|
65
|
-
minHeight: '44px',
|
|
66
|
-
justifyContent: 'center',
|
|
67
|
-
WebkitTapHighlightColor: 'transparent',
|
|
68
|
-
touchAction: 'manipulation',
|
|
69
|
-
}, children: [avatarUrl ? (_jsx("img", { src: avatarUrl, alt: "", width: 24, height: 24, style: {
|
|
70
|
-
width: '24px',
|
|
71
|
-
height: '24px',
|
|
72
|
-
borderRadius: '50%',
|
|
73
|
-
objectFit: 'cover',
|
|
74
|
-
flexShrink: 0,
|
|
75
|
-
} })) : (_jsx("span", { "aria-hidden": "true", style: {
|
|
76
|
-
width: '24px',
|
|
77
|
-
height: '24px',
|
|
78
|
-
borderRadius: '50%',
|
|
79
|
-
flexShrink: 0,
|
|
80
|
-
display: 'inline-flex',
|
|
81
|
-
alignItems: 'center',
|
|
82
|
-
justifyContent: 'center',
|
|
83
|
-
background: 'var(--accent, #6b7280)',
|
|
84
|
-
color: '#fff',
|
|
85
|
-
fontFamily: '"Manrope", system-ui, sans-serif',
|
|
86
|
-
fontSize: '0.65rem',
|
|
87
|
-
fontWeight: 700,
|
|
88
|
-
lineHeight: 1,
|
|
89
|
-
}, children: initial })), _jsx("span", { style: {
|
|
90
|
-
fontFamily: '"Manrope", system-ui, sans-serif',
|
|
91
|
-
fontSize: '0.7rem',
|
|
92
|
-
fontWeight: 600,
|
|
93
|
-
color: 'var(--ink, #1a1a1a)',
|
|
94
|
-
whiteSpace: 'nowrap',
|
|
95
|
-
}, children: firstName })] }), open && (_jsx("div", { style: {
|
|
96
|
-
position: 'absolute',
|
|
97
|
-
top: '100%',
|
|
98
|
-
right: 0,
|
|
99
|
-
marginTop: '0.25rem',
|
|
100
|
-
background: 'var(--panel, #fff)',
|
|
101
|
-
border: '1px solid var(--line, #e5e5e5)',
|
|
102
|
-
borderRadius: '0.5rem',
|
|
103
|
-
boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
|
|
104
|
-
zIndex: 100,
|
|
105
|
-
minWidth: '7rem',
|
|
106
|
-
}, children: _jsx("button", { onClick: () => {
|
|
39
|
+
return (_jsxs("div", { ref: ref, className: "relative", children: [_jsxs("button", { onClick: () => setOpen((v) => !v), className: `${BARE_BUTTON} gap-[0.35rem]`, children: [avatarUrl ? (_jsx("img", { src: avatarUrl, alt: "", width: 24, height: 24, className: "size-6 shrink-0 rounded-full object-cover" })) : (_jsx("span", { "aria-hidden": "true", className: "inline-flex size-6 shrink-0 items-center justify-center rounded-full bg-accent text-[0.65rem] font-bold leading-none text-white", children: initial })), _jsx("span", { className: "whitespace-nowrap text-[0.7rem] font-semibold text-ink", children: firstName })] }), open && (_jsx("div", { className: "absolute right-0 top-full z-[100] mt-1 min-w-28 rounded-lg border border-line bg-panel shadow-[0_4px_12px_rgb(0_0_0/0.1)]", children: _jsx("button", { onClick: () => {
|
|
107
40
|
setOpen(false);
|
|
108
41
|
signOut();
|
|
109
|
-
},
|
|
110
|
-
display: 'flex',
|
|
111
|
-
alignItems: 'center',
|
|
112
|
-
width: '100%',
|
|
113
|
-
background: 'none',
|
|
114
|
-
border: 'none',
|
|
115
|
-
cursor: 'pointer',
|
|
116
|
-
fontFamily: '"Manrope", system-ui, sans-serif',
|
|
117
|
-
fontSize: '0.7rem',
|
|
118
|
-
fontWeight: 600,
|
|
119
|
-
color: 'var(--ink, #1a1a1a)',
|
|
120
|
-
padding: '0.5rem 0.75rem',
|
|
121
|
-
minHeight: '44px',
|
|
122
|
-
WebkitTapHighlightColor: 'transparent',
|
|
123
|
-
touchAction: 'manipulation',
|
|
124
|
-
}, children: "Sign out" }) }))] }));
|
|
42
|
+
}, className: "flex min-h-11 w-full cursor-pointer touch-manipulation items-center border-0 bg-transparent px-3 py-2 text-[0.7rem] font-semibold text-ink [-webkit-tap-highlight-color:transparent]", children: "Sign out" }) }))] }));
|
|
125
43
|
}
|
|
126
44
|
//# sourceMappingURL=GameAuth.js.map
|
package/dist/GameAuth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GameAuth.js","sourceRoot":"","sources":["../src/GameAuth.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ;IACtB,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC;IACrD,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAEzC,uCAAuC;IACvC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,MAAM,OAAO,GAAG,CAAC,CAAa,EAAE,EAAE;YAChC,IAAI,GAAG,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAc,CAAC,EAAE,CAAC;gBAC3D,OAAO,CAAC,KAAK,CAAC,CAAC;YACjB,CAAC;QACH,CAAC,CAAC;QACF,QAAQ,CAAC,gBAAgB,CAAC,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACxD,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAC1E,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,IAAI,OAAO;QAAE,OAAO,eAAO,CAAC;IAE5B,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,OAAO,CACL,
|
|
1
|
+
{"version":3,"file":"GameAuth.js","sourceRoot":"","sources":["../src/GameAuth.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,iFAAiF;AACjF,MAAM,WAAW,GACf,uKAAuK,CAAC;AAE1K;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ;IACtB,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC;IACrD,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAEzC,uCAAuC;IACvC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,MAAM,OAAO,GAAG,CAAC,CAAa,EAAE,EAAE;YAChC,IAAI,GAAG,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAc,CAAC,EAAE,CAAC;gBAC3D,OAAO,CAAC,KAAK,CAAC,CAAC;YACjB,CAAC;QACH,CAAC,CAAC;QACF,QAAQ,CAAC,gBAAgB,CAAC,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACxD,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAC1E,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,IAAI,OAAO;QAAE,OAAO,eAAO,CAAC;IAE5B,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,OAAO,CACL,iBAAQ,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,WAAW,yCAAyC,wBAElF,CACV,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC;IACvD,sEAAsE;IACtE,6DAA6D;IAC7D,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;IACtC,MAAM,OAAO,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IAE3D,OAAO,CACL,eAAK,GAAG,EAAE,GAAG,EAAE,SAAS,EAAC,UAAU,aACjC,kBAAQ,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,WAAW,gBAAgB,aACjF,SAAS,CAAC,CAAC,CAAC,CACX,cACE,GAAG,EAAE,SAAS,EACd,GAAG,EAAC,EAAE,EACN,KAAK,EAAE,EAAE,EACT,MAAM,EAAE,EAAE,EACV,SAAS,EAAC,2CAA2C,GACrD,CACH,CAAC,CAAC,CAAC,CACF,8BACc,MAAM,EAClB,SAAS,EAAC,iIAAiI,YAE1I,OAAO,GACH,CACR,EACD,eAAM,SAAS,EAAC,wDAAwD,YAAE,SAAS,GAAQ,IACpF,EAER,IAAI,IAAI,CACP,cAAK,SAAS,EAAC,2HAA2H,YACxI,iBACE,OAAO,EAAE,GAAG,EAAE;wBACZ,OAAO,CAAC,KAAK,CAAC,CAAC;wBACf,OAAO,EAAE,CAAC;oBACZ,CAAC,EACD,SAAS,EAAC,sLAAsL,yBAGzL,GACL,CACP,IACG,CACP,CAAC;AACJ,CAAC"}
|
package/dist/GameButton.d.ts
CHANGED
|
@@ -15,7 +15,8 @@ export interface GameButtonProps {
|
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
17
|
* Prescribed game button. Touch-friendly (min 44px target), brand-consistent
|
|
18
|
-
* styling.
|
|
18
|
+
* styling. Four variants, three sizes — all opinionated, nothing custom.
|
|
19
|
+
* Utilities on the platform tokens; the press feedback is `active:scale`.
|
|
19
20
|
*/
|
|
20
21
|
export declare function GameButton({ children, variant, size, onClick, disabled, block, }: GameButtonProps): React.JSX.Element;
|
|
21
22
|
//# sourceMappingURL=GameButton.d.ts.map
|
package/dist/GameButton.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GameButton.d.ts","sourceRoot":"","sources":["../src/GameButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,GAAG,QAAQ,CAAC;AAC7E,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEhD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,SAAS,CAAC;IACpB,wCAAwC;IACxC,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,yEAAyE;IACzE,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kBAAkB;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;
|
|
1
|
+
{"version":3,"file":"GameButton.d.ts","sourceRoot":"","sources":["../src/GameButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,GAAG,QAAQ,CAAC;AAC7E,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEhD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,SAAS,CAAC;IACpB,wCAAwC;IACxC,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,yEAAyE;IACzE,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kBAAkB;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAeD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,OAAmB,EACnB,IAAW,EACX,OAAO,EACP,QAAgB,EAChB,KAAa,GACd,EAAE,eAAe,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAerC"}
|
package/dist/GameButton.js
CHANGED
|
@@ -1,76 +1,25 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
const SIZE = {
|
|
3
|
-
sm:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
fontSize: '0.9rem',
|
|
13
|
-
borderRadius: '0.75rem',
|
|
14
|
-
},
|
|
15
|
-
lg: {
|
|
16
|
-
minHeight: '3.5rem',
|
|
17
|
-
padding: '0.75rem 1.75rem',
|
|
18
|
-
fontSize: '1rem',
|
|
19
|
-
borderRadius: '0.875rem',
|
|
20
|
-
},
|
|
3
|
+
sm: 'min-h-11 px-4 py-2 text-[0.85rem] rounded-[0.625rem]',
|
|
4
|
+
md: 'min-h-12 px-5 py-[0.625rem] text-[0.9rem] rounded-xl',
|
|
5
|
+
lg: 'min-h-14 px-7 py-3 text-base rounded-[0.875rem]',
|
|
6
|
+
};
|
|
7
|
+
const VARIANT = {
|
|
8
|
+
primary: 'bg-accent text-white',
|
|
9
|
+
secondary: 'bg-panel text-ink ring-1 ring-inset ring-line',
|
|
10
|
+
ghost: 'bg-transparent text-muted',
|
|
11
|
+
danger: 'bg-danger text-white',
|
|
21
12
|
};
|
|
22
13
|
/**
|
|
23
14
|
* Prescribed game button. Touch-friendly (min 44px target), brand-consistent
|
|
24
|
-
* styling.
|
|
15
|
+
* styling. Four variants, three sizes — all opinionated, nothing custom.
|
|
16
|
+
* Utilities on the platform tokens; the press feedback is `active:scale`.
|
|
25
17
|
*/
|
|
26
18
|
export function GameButton({ children, variant = 'primary', size = 'md', onClick, disabled = false, block = false, }) {
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
justifyContent: 'center',
|
|
33
|
-
gap: '0.5rem',
|
|
34
|
-
minHeight: s.minHeight,
|
|
35
|
-
padding: s.padding,
|
|
36
|
-
fontSize: s.fontSize,
|
|
37
|
-
fontFamily: '"Manrope", system-ui, sans-serif',
|
|
38
|
-
fontWeight: 700,
|
|
39
|
-
lineHeight: 1,
|
|
40
|
-
borderRadius: s.borderRadius,
|
|
41
|
-
border: 'none',
|
|
42
|
-
cursor: disabled ? 'default' : 'pointer',
|
|
43
|
-
opacity: disabled ? 0.4 : 1,
|
|
44
|
-
transition: 'transform 120ms ease, opacity 120ms ease',
|
|
45
|
-
WebkitTapHighlightColor: 'transparent',
|
|
46
|
-
touchAction: 'manipulation',
|
|
47
|
-
};
|
|
48
|
-
const variantStyles = {
|
|
49
|
-
primary: {
|
|
50
|
-
background: 'var(--accent, #10b981)',
|
|
51
|
-
color: '#fff',
|
|
52
|
-
},
|
|
53
|
-
secondary: {
|
|
54
|
-
background: 'var(--panel, #f5f3f0)',
|
|
55
|
-
color: 'var(--ink, #1a1a1a)',
|
|
56
|
-
boxShadow: 'inset 0 0 0 1px var(--line, #e5e5e5)',
|
|
57
|
-
},
|
|
58
|
-
ghost: {
|
|
59
|
-
background: 'transparent',
|
|
60
|
-
color: 'var(--muted, #6b7280)',
|
|
61
|
-
},
|
|
62
|
-
danger: {
|
|
63
|
-
background: '#dc2626',
|
|
64
|
-
color: '#ffffff',
|
|
65
|
-
},
|
|
66
|
-
};
|
|
67
|
-
return (_jsx("button", { style: { ...base, ...variantStyles[variant] }, onClick: disabled ? undefined : onClick, disabled: disabled, onPointerDown: (e) => {
|
|
68
|
-
if (!disabled)
|
|
69
|
-
e.currentTarget.style.transform = 'scale(0.96)';
|
|
70
|
-
}, onPointerUp: (e) => {
|
|
71
|
-
e.currentTarget.style.transform = '';
|
|
72
|
-
}, onPointerLeave: (e) => {
|
|
73
|
-
e.currentTarget.style.transform = '';
|
|
74
|
-
}, children: children }));
|
|
19
|
+
const layout = block ? 'flex w-full' : 'inline-flex';
|
|
20
|
+
const state = disabled
|
|
21
|
+
? 'cursor-default opacity-40'
|
|
22
|
+
: 'cursor-pointer enabled:active:scale-[0.96]';
|
|
23
|
+
return (_jsx("button", { className: `${layout} ${SIZE[size]} ${VARIANT[variant]} ${state} touch-manipulation items-center justify-center gap-2 border-0 font-bold leading-none transition-[transform,opacity] duration-[120ms] [-webkit-tap-highlight-color:transparent]`, onClick: disabled ? undefined : onClick, disabled: disabled, children: children }));
|
|
75
24
|
}
|
|
76
25
|
//# sourceMappingURL=GameButton.js.map
|
package/dist/GameButton.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GameButton.js","sourceRoot":"","sources":["../src/GameButton.tsx"],"names":[],"mappings":";AAkBA,MAAM,IAAI,
|
|
1
|
+
{"version":3,"file":"GameButton.js","sourceRoot":"","sources":["../src/GameButton.tsx"],"names":[],"mappings":";AAkBA,MAAM,IAAI,GAAmC;IAC3C,EAAE,EAAE,sDAAsD;IAC1D,EAAE,EAAE,sDAAsD;IAC1D,EAAE,EAAE,iDAAiD;CACtD,CAAC;AAEF,MAAM,OAAO,GAAsC;IACjD,OAAO,EAAE,sBAAsB;IAC/B,SAAS,EAAE,+CAA+C;IAC1D,KAAK,EAAE,2BAA2B;IAClC,MAAM,EAAE,sBAAsB;CAC/B,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,EACzB,QAAQ,EACR,OAAO,GAAG,SAAS,EACnB,IAAI,GAAG,IAAI,EACX,OAAO,EACP,QAAQ,GAAG,KAAK,EAChB,KAAK,GAAG,KAAK,GACG;IAChB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC;IACrD,MAAM,KAAK,GAAG,QAAQ;QACpB,CAAC,CAAC,2BAA2B;QAC7B,CAAC,CAAC,4CAA4C,CAAC;IAEjD,OAAO,CACL,iBACE,SAAS,EAAE,GAAG,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,KAAK,iLAAiL,EAChP,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,EACvC,QAAQ,EAAE,QAAQ,YAEjB,QAAQ,GACF,CACV,CAAC;AACJ,CAAC"}
|
package/dist/GameConfirm.d.ts
CHANGED
|
@@ -12,5 +12,5 @@ export interface GameConfirmProps {
|
|
|
12
12
|
* Confirm/cancel dialog for games. Built on GameModal.
|
|
13
13
|
* Use for: quit game, restart, delete save, etc.
|
|
14
14
|
*/
|
|
15
|
-
export declare function GameConfirm({ open, title, message, confirmLabel, cancelLabel, onConfirm, onCancel, variant, }: GameConfirmProps): import("react
|
|
15
|
+
export declare function GameConfirm({ open, title, message, confirmLabel, cancelLabel, onConfirm, onCancel, variant, }: GameConfirmProps): import("react").JSX.Element;
|
|
16
16
|
//# sourceMappingURL=GameConfirm.d.ts.map
|