@trading-game/design-intelligence-layer 0.17.3 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/AGENTS.md +104 -231
  2. package/README.md +46 -742
  3. package/dist/index.cjs +2753 -2985
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.cts +247 -261
  6. package/dist/index.d.ts +247 -261
  7. package/dist/index.js +2648 -2855
  8. package/dist/index.js.map +1 -1
  9. package/docs/components/accordion.md +85 -0
  10. package/docs/components/alert-dialog.md +98 -0
  11. package/docs/components/aspect-ratio.md +55 -0
  12. package/docs/components/avatar.md +88 -0
  13. package/docs/components/badge.md +75 -0
  14. package/docs/components/banner.md +84 -0
  15. package/docs/components/bottom-navigation.md +90 -0
  16. package/docs/components/breadcrumb.md +85 -0
  17. package/docs/components/button.md +94 -0
  18. package/docs/components/calendar.md +74 -0
  19. package/docs/components/card.md +79 -0
  20. package/docs/components/carousel.md +82 -0
  21. package/docs/components/checkbox.md +66 -0
  22. package/docs/components/chip.md +72 -0
  23. package/docs/components/command.md +86 -0
  24. package/docs/components/context-menu.md +90 -0
  25. package/docs/components/dialog.md +95 -0
  26. package/docs/components/drawer.md +97 -0
  27. package/docs/components/dropdown-menu.md +92 -0
  28. package/docs/components/empty.md +87 -0
  29. package/docs/components/field.md +117 -0
  30. package/docs/components/hover-card.md +77 -0
  31. package/docs/components/input-group.md +105 -0
  32. package/docs/components/input-otp.md +87 -0
  33. package/docs/components/input.md +71 -0
  34. package/docs/components/item.md +105 -0
  35. package/docs/components/label.md +56 -0
  36. package/docs/components/link.md +66 -0
  37. package/docs/components/menubar.md +102 -0
  38. package/docs/components/native-select.md +71 -0
  39. package/docs/components/navigation-button.md +68 -0
  40. package/docs/components/navigation-menu.md +99 -0
  41. package/docs/components/numpad.md +78 -0
  42. package/docs/components/pagination.md +84 -0
  43. package/docs/components/popover.md +89 -0
  44. package/docs/components/profile-photo.md +81 -0
  45. package/docs/components/progress.md +60 -0
  46. package/docs/components/radio-group.md +82 -0
  47. package/docs/components/resizable.md +79 -0
  48. package/docs/components/scroll-area.md +66 -0
  49. package/docs/components/section-message.md +99 -0
  50. package/docs/components/select.md +105 -0
  51. package/docs/components/separator.md +55 -0
  52. package/docs/components/sheet.md +91 -0
  53. package/docs/components/sidebar.md +125 -0
  54. package/docs/components/skeleton.md +51 -0
  55. package/docs/components/slider.md +61 -0
  56. package/docs/components/spinner.md +52 -0
  57. package/docs/components/stepper.md +68 -0
  58. package/docs/components/switch.md +57 -0
  59. package/docs/components/table.md +86 -0
  60. package/docs/components/tabs.md +95 -0
  61. package/docs/components/textarea.md +58 -0
  62. package/docs/components/toast.md +66 -0
  63. package/docs/components/toggle-group.md +77 -0
  64. package/docs/components/toggle.md +60 -0
  65. package/docs/components/tooltip.md +83 -0
  66. package/docs/foundations/colors.md +109 -0
  67. package/docs/foundations/motion.md +63 -0
  68. package/docs/foundations/shape-layout.md +56 -0
  69. package/docs/foundations/typography.md +83 -0
  70. package/docs/patterns/forms.md +70 -0
  71. package/docs/patterns/menus.md +50 -0
  72. package/docs/patterns/on-brand.md +43 -0
  73. package/guides/audits/design-system-audit-2026-07.md +135 -0
  74. package/guides/rules/design-system-consuming-project.mdc +56 -0
  75. package/package.json +6 -7
  76. package/src/styles.css +1616 -251
  77. package/guides/design-system-guide/trading-game-ds-guide.md +0 -917
package/README.md CHANGED
@@ -1,24 +1,20 @@
1
- # Trading Game Design System
1
+ # Champion Trade Design System
2
2
 
3
- A component library and design token system for the Trading Game product. Built on [shadcn/ui](https://ui.shadcn.com/) with a custom Figma-driven token architecture, Tailwind CSS v4, and React 19.
3
+ The component library and token architecture for **Champion Trade** (formerly Trading Game) — a Tailwind CSS v4 + React design system with a strict semantic token layer, per-component typography tokens, light and dark themes, and 57 production components.
4
4
 
5
- Published as **`@trading-game/design-intelligence-layer`** on npm.
5
+ > **Package name:** still published as `@trading-game/design-intelligence-layer`. The npm scope renames to Champion Trade at v1.0 — until then, install and import under the current name below.
6
6
 
7
- ---
8
-
9
- ## Quick Start
10
-
11
- ### Installation
7
+ ## Install
12
8
 
13
9
  ```bash
14
10
  npm install @trading-game/design-intelligence-layer@latest --safe-chain-skip-minimum-package-age
15
11
  ```
16
12
 
17
- > **Note:** The `--safe-chain-skip-minimum-package-age` flag bypasses npm's 72-hour safe-chain hold on newly published packages. Required when the latest version was published less than 72 hours ago (common for AI agents and CI pipelines).
13
+ > The `--safe-chain-skip-minimum-package-age` flag bypasses npm's 72-hour safe-chain hold on newly published packages — needed when the latest version is fresh (common for AI agents and CI).
18
14
 
19
- ### 1. Add the CSS imports
15
+ ### 1 · CSS imports
20
16
 
21
- In your main CSS file (e.g. `globals.css`), add these three lines **in this order**:
17
+ In your main CSS file (e.g. `globals.css`), **in this order**:
22
18
 
23
19
  ```css
24
20
  @import "@trading-game/design-intelligence-layer/styles";
@@ -26,11 +22,9 @@ In your main CSS file (e.g. `globals.css`), add these three lines **in this orde
26
22
  @source "../node_modules/@trading-game/design-intelligence-layer/dist";
27
23
  ```
28
24
 
29
- > **Why `@source`?** Tailwind v4 skips `node_modules` by default. Without this line, Tailwind won't generate CSS for the component classes. Adjust the path if your CSS file is not one level below `node_modules` (e.g. in a monorepo use `../../node_modules/...`).
30
-
31
- ### 1b. Vite projects — add the Tailwind plugin
25
+ > **Why `@source`?** Tailwind v4 skips `node_modules` by default; without it, no CSS is generated for component classes. Adjust the relative path in monorepos (`../../node_modules/...`).
32
26
 
33
- If your project uses **Vite** (e.g. Vite + React), you must add the Tailwind CSS plugin to your Vite config:
27
+ ### 1b · Vite projects
34
28
 
35
29
  ```bash
36
30
  npm install @tailwindcss/vite
@@ -41,29 +35,34 @@ npm install @tailwindcss/vite
41
35
  import react from '@vitejs/plugin-react'
42
36
  import tailwindcss from '@tailwindcss/vite'
43
37
 
44
- export default {
45
- plugins: [react(), tailwindcss()],
46
- }
38
+ export default { plugins: [react(), tailwindcss()] }
47
39
  ```
48
40
 
49
- > **Next.js projects** do not need this — Next.js uses PostCSS for Tailwind automatically.
41
+ Next.js needs no plugin (PostCSS handles Tailwind).
42
+
43
+ ### 2 · Fonts
50
44
 
51
- ### 2. Fonts (handled automatically)
45
+ The styles import loads **Plus Jakarta Sans** from Google Fonts automatically. `font-body`, `font-display`, and `font-sans` all resolve to it.
52
46
 
53
- The styles import loads **Plus Jakarta Sans** from Google Fonts automatically. No additional font setup is needed.
47
+ ### 3 · Dark theme
54
48
 
55
- Use `font-body`, `font-display`, or `font-sans` in your Tailwind classes — all resolve to Plus Jakarta Sans.
49
+ Add the `dark` class (or `data-theme="dark"`) to `<html>`. Every semantic token re-resolves — no component changes needed.
56
50
 
57
- ### 3. Use components
51
+ ### 4 · Use components
58
52
 
59
53
  ```tsx
60
- import { Button, Card, Badge } from "@trading-game/design-intelligence-layer"
54
+ import { Button, Card, Field, FieldLabel, FieldContent, Input } from "@trading-game/design-intelligence-layer"
61
55
 
62
56
  export default function App() {
63
57
  return (
64
58
  <Card>
65
- <Button variant="primary">Trade Now</Button>
66
- <Badge>Live</Badge>
59
+ <Field size="md">
60
+ <FieldLabel htmlFor="amount">Amount</FieldLabel>
61
+ <FieldContent>
62
+ <Input id="amount" placeholder="250.00" />
63
+ </FieldContent>
64
+ </Field>
65
+ <Button variant="primary">Trade now</Button>
67
66
  </Card>
68
67
  )
69
68
  }
@@ -71,734 +70,39 @@ export default function App() {
71
70
 
72
71
  ### Peer dependencies
73
72
 
74
- Make sure these are installed in your project:
75
-
76
- ```bash
77
- npm install react react-dom tailwindcss
78
- ```
79
-
80
- > Requires React 18+ and Tailwind CSS v4+.
81
-
82
- ---
73
+ React 18+, ReactDOM, Tailwind CSS v4+.
83
74
 
84
75
  ## What's inside
85
76
 
86
- - **56 UI components** — buttons, forms, dialogs, charts, sidebars, and more
87
- - **Design tokens** — CSS custom properties for color, radius, typography, and transitions synced from Figma
88
- - **Transition tokens** — semantic duration and easing tokens (`duration-base`, `ease-standard`, etc.) — no hardcoded `duration-200` anywhere
89
- - **Light-only theme** — white backgrounds, black text, primary blue accents — no dark mode
90
- - **Typography classes** — heading scale (h1–xs) and body scale (lg–xs) ready to use
91
- - **TypeScript** — full type definitions included
92
- - **ESM + CJS** — works with any bundler
77
+ - **57 components** — forms, overlays, menus, navigation, data display, plus trading-specific pieces (Numpad, Bottom Navigation, Profile Photo)
78
+ - **Three-layer token architecture** — primitives → semantics → component type tokens; components consume semantic colour tokens only
79
+ - **Light + dark themes** — dark is designed, not mirrored: its own surface mixes, selection fills, and ink steps
80
+ - **No shadows** — elevation is hairlines and surface steps
81
+ - **Pill shape system** — buttons and chips are pills, icon buttons are circles, cards 18px, menus 10px
82
+ - **TypeScript, ESM + CJS**
93
83
 
94
- ---
84
+ ## Documentation
95
85
 
96
- ## Available components
97
-
98
- | Component | Import |
86
+ | | |
99
87
  |---|---|
100
- | Accordion | `Accordion, AccordionItem, AccordionTrigger, AccordionContent` |
101
- | Alert | `Alert` — dismissible status notification. Variants: `info`, `warning`, `danger`, `success`. Layouts: `inline`, `card`. Slots: `title`, `description`, `icon`, `action`, `actions`, `onDismiss`. |
102
- | Alert Dialog | `AlertDialog, AlertDialogTrigger, AlertDialogContent, ...` |
103
- | Aspect Ratio | `AspectRatio` |
104
- | Avatar | `Avatar, AvatarImage, AvatarFallback, AvatarBadge, AvatarGroup` |
105
- | Badge | `Badge, badgeVariants` |
106
- | Banner | `Banner` — dismissible promotional surface on a tinted primary surface. Brand-blue square `IconBadge` (40 px, `rounded-xs`) with a swappable `glyph` (or pass `icon` to override the badge entirely). Layouts: `inline`, `card`. Slots: `title`, `description`, `icon`/`glyph`, `action`, `actions`, `onDismiss`, `onClick` (whole-banner clickable). |
107
- | Breadcrumb | `Breadcrumb, BreadcrumbList, BreadcrumbItem, ...` |
108
- | Button | `Button, buttonVariants` |
109
- | Calendar | `Calendar, CalendarDayButton` |
110
- | Card | `Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter` |
111
- | Carousel | `Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext` |
112
- | Chart | `ChartContainer, ChartTooltip, ChartLegend, ChartStyle` |
113
- | Checkbox | `Checkbox` |
114
- | Chip | `Chip` — pill-shaped, toggle-able selection chip (e.g. game filters). Selected state takes `bg-primary/[0.08]` + `text-primary`; unselected uses outline + `text-on-prominent`. |
115
- | Collapsible | `Collapsible, CollapsibleTrigger, CollapsibleContent` |
116
- | Combobox | `Combobox, ComboboxInput, ComboboxContent, ComboboxItem, ...` |
117
- | Command | `Command, CommandDialog, CommandInput, CommandList, ...` |
118
- | Context Menu | `ContextMenu, ContextMenuTrigger, ContextMenuContent, ...` |
119
- | Dialog | `Dialog, DialogTrigger, DialogContent, DialogHeader, ...` |
120
- | Drawer | `Drawer, DrawerTrigger, DrawerContent, DrawerHeader, ...` |
121
- | Dropdown Menu | `DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, ...` |
122
- | Empty State | `Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyContent` |
123
- | Field | `Field, FieldLabel, FieldDescription, FieldError, FieldGroup` |
124
- | Form | `Form, FormItem, FormLabel, FormControl, FormField, FormMessage` |
125
- | Hover Card | `HoverCard, HoverCardTrigger, HoverCardContent` |
126
- | Input | `Input` — variants: `default` (light surfaces), `inverse` (dark sunken fill + white text, for use on `bg-primary` / brand-coloured surfaces). |
127
- | Input Group | `InputGroup, InputGroupAddon, InputGroupButton, InputGroupText` — variants: `default`, `inverse` (propagates to nested `InputGroupInput` / `InputGroupAddon` / `InputGroupText` via context). |
128
- | Input OTP | `InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator` — Variants: OTP 4 digits, OTP 6 digits, OTP 6 split, OTP 8 digits, OTP 6 separated |
129
- | Item | `Item, ItemMedia, ItemContent, ItemTitle, ItemDescription` |
130
- | Kbd | `Kbd, KbdGroup` |
131
- | Label | `Label` |
132
- | Link | `Link` |
133
- | Menubar | `Menubar, MenubarMenu, MenubarTrigger, MenubarContent, ...` |
134
- | Native Select | `NativeSelect, NativeSelectOptGroup, NativeSelectOption` |
135
- | Navigation Button | `NavigationButton, navigationButtonVariants` |
136
- | Navigation Menu | `NavigationMenu, NavigationMenuList, NavigationMenuTrigger, ...` |
137
- | Pagination | `Pagination, PaginationContent, PaginationLink, ...` |
138
- | Popover | `Popover, PopoverTrigger, PopoverContent, PopoverAnchor` |
139
- | Progress | `Progress` |
140
- | Radio Group | `RadioGroup, RadioGroupItem` |
141
- | Resizable | `ResizableHandle, ResizablePanel, ResizablePanelGroup` |
142
- | Scroll Area | `ScrollArea, ScrollBar` |
143
- | Select | `Select, SelectTrigger, SelectContent, SelectItem, ...` |
144
- | Separator | `Separator` |
145
- | Sheet | `Sheet, SheetTrigger, SheetContent, SheetHeader, ...` |
146
- | Sidebar | `Sidebar, SidebarProvider, SidebarMenu, SidebarMenuItem, ...` |
147
- | Skeleton | `Skeleton` |
148
- | Slider | `Slider` |
149
- | Spinner | `Spinner` |
150
- | Stepper | `Stepper` |
151
- | Switch | `Switch` |
152
- | Table | `Table, TableHeader, TableBody, TableRow, TableHead, TableCell` |
153
- | Tabs | `Tabs, TabsList, TabsTrigger, TabsContent` |
154
- | Textarea | `Textarea` |
155
- | Ticket Card | `TicketCard`, `CreditTicketCard` |
156
- | Toast | `Toaster` |
157
- | Toggle | `Toggle, toggleVariants` |
158
- | Toggle Group | `ToggleGroup, ToggleGroupItem` |
159
- | Tooltip | `Tooltip, TooltipTrigger, TooltipContent, TooltipProvider` |
160
-
161
- ---
162
-
163
- ## Blocks
164
-
165
- Blocks are opinionated, composed UI patterns built from design system primitives — **exported from the package the same as primitives**. Import them by name and pass data via props. Variants (`layout` / `mode` / `status`) live on a single block, not separate components.
166
-
167
- ```tsx
168
- import { HeroBlock, ResultBlock, OpenPositionsBlock } from "@trading-game/design-intelligence-layer"
169
- ```
170
-
171
- | Block | Variants | Description |
172
- |---|---|---|
173
- | `HeroBlock` | `layout: "centered" \| "split"` | Marketing hero section. Centred = single-column, no image; split = two-column with image. Tagline pill, heading, body, primary + optional secondary CTA. |
174
- | `AuthBlock` | `mode: "sign-in" \| "sign-up"` | Auth form — logo lockup, OAuth provider buttons (Google, Telegram, X), divider, email input, terms, primary CTA, footer cross-link. |
175
- | `FAQBlock` | `layout: "desktop" \| "mobile"` | Eyebrow + heading + intro + accordion + optional "Need more help" card. Items passed as `{ value, question, answer }[]`. |
176
- | `NavBarBlock` | — (internal mobile-menu state) | Responsive marketing nav — brand logos, link buttons, Sign in / Sign up CTAs, hamburger drawer on mobile. |
177
- | `HeaderNavigationBlock` | — | In-app trading header. Back · badge · balance · optional history button with `history.count` numeric badge (`bg-primary`; ≥99 → `99+`) · actions slot. |
178
- | `OpenPositionsBlock` | `Position` discriminated union (`rise-fall` / `swipe` / `box-o` / `digits`) | Responsive Sheet (desktop) / Drawer (mobile) listing open positions. Consumer-provided trigger; default empty state. |
179
- | `ResultBlock` | `status: "win" \| "loss"`, `ctaMode: "next-round" \| "go-again" \| "conversion"` | Win/loss result card. Animated halo + thumb illustration (bundled inline) + contract badge + duration badge + CTA. |
180
- | `ResultDialog` | — | Fixed-width system dialog (title + body + two CTAs). For messages like "out of balance" or "on a roll". |
181
-
182
- See [`AGENTS.md`](./AGENTS.md#blocks) for full usage examples per block.
183
-
184
- ---
185
-
186
- ## Button variants and sizes
187
-
188
- ```tsx
189
- <Button variant="primary" /> // Blue filled — main CTA (light surfaces)
190
- <Button variant="primary-inverse" /> // White filled + blue text — main CTA on dark/coloured surfaces
191
- <Button variant="primary-shimmer" /> // Same as `primary` + sweeping diagonal shimmer (headline CTAs that need to draw the eye)
192
- <Button variant="primary-inverse-shimmer" /> // Same as `primary-inverse` + brand-blue shimmer over the white surface
193
- <Button variant="secondary" /> // Black outline — secondary actions (light surfaces)
194
- <Button variant="secondary-inverse" /> // White outline + white text — secondary actions on dark/coloured surfaces
195
- <Button variant="tertiary" /> // Text only — minimal (light surfaces)
196
- <Button variant="tertiary-inverse" /> // Text only + white text — minimal on dark/coloured surfaces
197
-
198
- // Sizes
199
- <Button size="lg" /> // 48px height (default)
200
- <Button size="md" /> // 40px height
201
- <Button size="sm" /> // 32px height
202
- <Button size="xs" /> // 24px height
203
- <Button size="icon-lg" /> // 48px square
204
- <Button size="icon-md" /> // 40px square
205
- <Button size="icon-sm" /> // 28px square
206
- <Button size="icon-xs" /> // 24px square
207
- ```
208
-
209
- ## Navigation Button
210
-
211
- An icon-only tertiary button for navigation actions. Transparent background, black icon color (`text-on-prominent`), grey hover (`bg-secondary-hover`). All sizes follow the 8-point grid.
212
-
213
- ```tsx
214
- <NavigationButton size="lg" aria-label="Home"><HomeIcon /></NavigationButton>
215
- ```
216
-
217
- | Size | Button | Icon |
218
- |------|--------|------|
219
- | `lg` | 48px | 24px |
220
- | `md` | 40px | 20px |
221
- | `sm` | 32px | 20px |
222
- | `xs` | 24px | 16px |
223
-
224
- States: hover (`bg-secondary-hover`), focus (3px `ring-ring/50`), active (`opacity-60`), disabled (`opacity-24`). Supports `asChild` for custom elements.
225
-
226
- ## Badge variants
227
-
228
- ```tsx
229
- // Default (solid)
230
- <Badge variant="default" /> // Blue (#2323FF)
231
- <Badge variant="default-success" /> // Green
232
- <Badge variant="default-fail" /> // Red
233
-
234
- // Standard (neutral grey chip) — for non-status meta like contract type, duration
235
- <Badge variant="standard" />
236
-
237
- // Fill (tint background)
238
- <Badge variant="fill" /> // Blue tint
239
- <Badge variant="fill-success" /> // Green tint
240
- <Badge variant="fill-fail" /> // Red tint
241
- <Badge variant="fill-warning" /> // Yellow tint — status/warning
242
- <Badge variant="fill-credit" /> // Frosted-glass surface (`--semantic-boost-surface` = `oklch(1 0 0 / 0.22)`) + white text + `backdrop-blur-md` — Welcome credit / bonus chip. Only readable on coloured surfaces (e.g. a brand-blue hero card); blends into white surfaces by design.
243
- <Badge variant="fill-demo" /> // Peach surface + red-orange text — demo/test-mode flags
244
-
245
- // Outline (black border, secondary-hover on hover)
246
- <Badge variant="outline" />
247
-
248
- // Ghost (transparent)
249
- <Badge variant="ghost" />
250
- <Badge variant="ghost-success" />
251
- <Badge variant="ghost-fail" />
252
-
253
- // Sizes
254
- <Badge size="sm" /> // 24px height
255
- <Badge size="md" /> // 32px height (default)
256
- <Badge size="lg" /> // 40px height
257
- ```
258
-
259
- ## Alert variants
260
-
261
- Dismissible **status** notification (info / warning / danger / success). For promotional / marketing surfaces, use **Banner** instead.
262
-
263
- ```tsx
264
- // Variant — controls the surface tint and icon accent
265
- <Alert variant="info" /> // sky-blue tint
266
- <Alert variant="warning" /> // yellow tint
267
- <Alert variant="danger" /> // red tint
268
- <Alert variant="success" /> // green tint
269
-
270
- // Layout — controls the shape
271
- <Alert layout="inline" /> // single row: icon · body · optional action · close X (default)
272
- <Alert layout="card" /> // taller: icon · body · close X top-right, up to 2 buttons bottom-left
273
-
274
- // Slots — all optional (at least one of title or description is required)
275
- <Alert
276
- variant="info"
277
- layout="inline"
278
- title="Verify your email"
279
- description="We sent a confirmation link to example@email.com."
280
- icon={<MyIcon />} // pass null to omit; omit to use the variant default
281
- action="Resend" // inline only — string is wrapped in Button automatically
282
- onDismiss={() => setOpen(false)} // when provided, close X is rendered
283
- dismissLabel="Dismiss" // aria-label for the close X
284
- />
285
-
286
- <Alert
287
- variant="info"
288
- layout="card"
289
- title="..."
290
- description="..."
291
- actions={["Learn more", "Open trading"]} // card only — array of up to 2 buttons
292
- onDismiss={() => setOpen(false)}
293
- />
294
- ```
295
-
296
- **Behaviour:**
297
- - Dismissal is **controlled** — clicking the close X calls `onDismiss`; the parent owns visibility.
298
- - All variants' buttons default to `Button variant="secondary"`. Pass a fully-styled `<Button>` to `action` / `actions` to override.
299
- - Inline + single-line content (only title OR only description) renders at a fixed 48px height with everything vertically centered. Inline + multi-line and card layouts grow to fit.
300
- - `role="alert"` + `aria-live="assertive"` for `danger` / `warning`; `role="status"` + `aria-live="polite"` for `info` / `success`.
301
-
302
- ## Banner
303
-
304
- Dismissible **promotional / marketing** surface — for feature launches, offers, onboarding nudges, cross-promo. Single visual treatment (no severity variants); for status messages use **Alert** instead.
305
-
306
- ```tsx
307
- // Layout — controls the shape
308
- <Banner layout="inline" /> // single row: icon · body · optional action · close X (default)
309
- <Banner layout="card" /> // body on row 1, up to 2 action buttons on row 2 bottom-left
310
-
311
- // Slots — all optional (at least one of title or description is required)
312
- <Banner
313
- layout="inline"
314
- title={<>Spring release <b>is here.</b></>} // wrap emphasis in <b> for brand-blue accent
315
- description="New features just landed — take a look at what's changed."
316
- glyph={<Sparkles className="size-5" />} // swaps the icon *inside* the default brand badge (default <Gift />)
317
- icon={<MyIcon />} // full override — replaces the brand badge entirely; null to omit
318
- action="Get started" // string wrapped in Button variant="primary"
319
- onDismiss={() => setOpen(false)} // when provided, close X is rendered
320
- dismissLabel="Dismiss" // aria-label for the close X
321
- />
322
-
323
- <Banner
324
- layout="card"
325
- title="..."
326
- description="..."
327
- actions={["Learn more", "Get started"]} // first = Secondary, second = Primary
328
- onDismiss={() => setOpen(false)}
329
- />
330
-
331
- // Whole-banner clickable — the surface itself is the CTA.
332
- // Mutually exclusive with action / actions (buttons dropped, dev warning).
333
- <Banner
334
- layout="inline"
335
- title="Tap anywhere on this banner"
336
- description="Whole surface is the click target."
337
- onClick={() => navigate("/promo")}
338
- onDismiss={() => setOpen(false)} // close X still works independently
339
- />
340
- ```
341
-
342
- **Visual:**
343
- - Surface: tinted primary surface — `bg-primary/[0.08]` with `border-primary/[0.16]` edge, `rounded-sm` (6 px — matches Card and Alert).
344
- - Default icon: 40 px brand-blue **square** (`rounded-xs bg-primary`, 4 px corners) with a white `Gift` glyph inside. Swap just the glyph via the `glyph` prop (e.g. `<Sparkles className="size-5" />`); replace the entire badge via the `icon` prop; or `icon={null}` to omit the column entirely.
345
- - **Multi-line layout** (description set, or `layout="card"`): the leading icon top-aligns with the title (grid uses `items-start`) so it sits beside the headline rather than floating mid-body. Single-line banners stay vertically centred (`items-center`).
346
- - Default action button: `Button variant="secondary"` size `sm` for every slot (inline `action` and both `actions[]` in card). Pass `<Button>` elements explicitly to override variant/size.
347
- - Title supports `<b>` children styled with `text-primary` for in-line brand-blue accent.
348
- - Close X (32 px): vertically-centred in the grid for single-line banners; pinned to the **top-right corner** (`absolute top-3 right-3`) when the banner is multi-line (`description` set) or `layout="card"` — so it reads as a true corner dismiss instead of floating mid-body. Hovers to `bg-secondary-hover`.
349
-
350
- ## Component behaviour notes
351
-
352
- ### AlertDialog — button fill
353
- `AlertDialogAction` and `AlertDialogCancel` fill the full container width **only** when `size="sm"`. On `size="default"` (the default), buttons are natural width and right-aligned.
354
-
355
- ```tsx
356
- <AlertDialogContent size="sm"> // buttons fill width (50/50 grid)
357
- <AlertDialogContent size="default"> // buttons natural width, right-aligned
358
- ```
359
-
360
- ### Drawer — footer buttons
361
- `DrawerFooter` makes all direct child buttons fill the full width automatically via `[&>*]:w-full`. Stack your buttons inside `DrawerFooter` and they will always be full-width.
362
-
363
- ### Link — size lg
364
- The `lg` variant of `Link` uses `text-base` (16px). The `md` and `sm` variants use `text-sm` (14px) and `text-xs` (12px) respectively.
365
-
366
- ```tsx
367
- <Link size="lg" /> // 16px — font-base
368
- <Link size="md" /> // 14px — font-sm
369
- <Link size="sm" /> // 12px — font-xs
370
- ```
371
-
372
- ### Select — sizes
373
- `SelectTrigger` accepts a `size` prop with three values matching the `Input` height scale:
374
-
375
- ```tsx
376
- <SelectTrigger size="sm" /> // h-8 (32px) — text-xs
377
- <SelectTrigger size="md" /> // h-10 (40px) — text-sm (default)
378
- <SelectTrigger size="lg" /> // h-12 (48px) — text-base
379
- ```
380
-
381
- ### Tabs — line variant hover
382
- On the `line` variant, hover state shows `text-primary` (blue) with no background fill. On the `default` (pill) variant, hover shows a grey background with `text-on-prominent`.
383
-
384
- ### Link — underline
385
- Underline is **always visible** (not hover-only). Class chain: `underline decoration-2 underline-offset-4 text-primary`.
386
-
387
- ### Switch — pill + circle
388
- Track is a pill (`rounded-full`); thumb is always a white circle (`rounded-full bg-prominent`). Checked track: `bg-primary`. Unchecked track: `bg-input`.
389
-
390
- ### Tooltip — default surface
391
- Default variant uses a **black** surface (`bg-prominent-inverse text-on-prominent-inverse`) with a matching arrow. Use `variant="inverse"` for a white tooltip when the trigger sits on a saturated coloured surface. The primitive bakes in `max-w-xs` + `whitespace-normal`, so consumers no longer need to pass them. When a `Kbd` is detected inside via `:has([data-slot=kbd])`, padding bumps to `px-3.5 py-2` and siblings receive `space-y-1.5` for breathing room.
392
-
393
- ### Sidebar — selection + padding
394
- `SidebarHeader` / `SidebarFooter` / `SidebarGroup` use `px-layout-margin-inline py-2` (16 px horizontal via `--semantic-layout-margin-inline`), with `group-data-[collapsible=icon]:px-2` so the collapsed icon rail tightens to 8 px. Active selection: `bg-primary/10` + `text-primary` + `font-semibold`. For `size="lg"` menu buttons (typical brand-block + label or profile pattern), the collapsed state uses `p-0! justify-center` so a 32 px brand block fills the rail while a 16 px lucide icon still centres on the same vertical line.
395
-
396
- ### Button — bg transition
397
- The button base sets `transition-[color,border-color,opacity,box-shadow,transform]` (not `transition-all`), so `background-color` flips instantly on hover instead of fading in over 150 ms. This prevents the brief partially-transparent phase where surrounding surface tints (e.g. an Alert) could bleed through a hovered secondary button.
398
-
399
- ---
400
-
401
- ## Design tokens
402
-
403
- All tokens are CSS custom properties, loaded automatically via `@trading-game/design-intelligence-layer/styles`.
404
-
405
- ### Background tokens
406
-
407
- | Tailwind class | CSS variable | Value | Usage |
408
- |---|---|---|---|
409
- | `bg-prominent` | `--prominent` | `#FFFFFF` | Page background |
410
- | `bg-prominent-inverse` | `--prominent-inverse` | `#000000` | Dark surface for use on light pages (e.g. toasts) |
411
- | `bg-card` | `--card` | `#FFFFFF` | Card/panel surface |
412
- | `bg-popover` | `--popover` | `#FFFFFF` | Popover/dropdown surface |
413
- | `bg-subtle` | `--subtle` | `#F5F5F5` | Subtle tinted surface |
414
- | `bg-overlay` | `--overlay` | black 50% | Modal/dialog backdrop only |
415
- | `bg-tabs` | `--tabs` | black 4% alpha | Tab container background (`variant="default"`) |
416
- | `bg-tabs-active` | `--tabs-active` | `#FFFFFF` | Active / selected tab pill |
417
- | `bg-primary` | `--primary` | `#2323FF` | Brand blue — CTAs |
418
- | `bg-primary-hover` | `--primary-hover` | `#0B0BD2` | Primary button hover (darker blue) |
419
- | `bg-primary-inverse` | `--primary-inverse` | `#FFFFFF` | Inverted primary surface — white bg for use on dark/coloured areas |
420
- | `bg-secondary-hover` | `--secondary-hover` | `#EEEEEE` | Outline/secondary hover (light grey) |
421
- | `bg-semantic-win` | `--semantic-win` | green | Profit / positive state |
422
- | `bg-semantic-loss` | `--semantic-loss` | red | Loss / negative state |
423
- | `bg-semantic-boost` | `--semantic-boost` | `#F9840F` amber-orange | Boost / bonus badge background |
424
-
425
- ### Text tokens
426
-
427
- > **Naming convention:** every surface (bg) token has a paired `text-on-<surface>` foreground (text/icon) token. Always use the pair together — `bg-primary` + `text-on-primary`, `bg-semantic-win` + `text-on-semantic-win`, etc. — for guaranteed legibility regardless of theme.
428
-
429
- | Tailwind class | Value | Usage |
430
- |---|---|---|
431
- | `text-on-prominent` | `#000000` | Text on `bg-prominent` (default body text on light surfaces) |
432
- | `text-on-prominent-inverse` | `#FFFFFF` | Text on `bg-prominent-inverse` (e.g. toast title/description) |
433
- | `text-on-subtle` | mid grey | Text on `bg-subtle` (secondary / supporting text) |
434
- | `text-on-disabled` | `#BBBBBB` | Disabled / inactive text |
435
- | `text-on-primary` | `#FFFFFF` | Text on `bg-primary` (e.g. primary button label, tooltip text) |
436
- | `text-on-primary-inverse` | `#2323FF` | Text on `bg-primary-inverse` (e.g. primary-inverse button label) |
437
- | `text-on-semantic-win` | `#FFFFFF` | Text on `bg-semantic-win` (e.g. success badge label) |
438
- | `text-on-semantic-loss` | `#FFFFFF` | Text on `bg-semantic-loss` (e.g. fail badge label) |
439
- | `text-on-semantic-warning` | `#FFFFFF` | Text on `bg-semantic-warning` (e.g. warning badge label) |
440
- | `text-on-semantic-boost` | `#713813` dark amber | Text on `bg-semantic-boost` (e.g. boost badge label) |
441
- | `text-primary` | `#2323FF` | Brand blue inline text (use as a foreground over neutral surfaces) |
442
- | `text-semantic-win` | green | Profit / positive inline text |
443
- | `text-semantic-loss` | red | Loss / negative inline text |
444
-
445
- > **Deprecated:** `text-on-prominent-static-inverse` and the matching `--on-prominent-static-inverse` CSS variable are still defined for backwards-compat with external code that references them via raw `var()`. Internal components no longer use them. For new code, pick the explicit `text-on-<surface>` paired with the actual background.
446
-
447
- ### Border & focus tokens
448
-
449
- | Tailwind class | CSS variable | Value | Usage |
450
- |---|---|---|---|
451
- | `border-border-subtle` | `--border-subtle` | `#EEEEEE` | **Default** — cards, dividers, form borders |
452
- | `border-border-prominent` | `--border-prominent` | `#000000` | Outline variant components (button, badge, toggle) |
453
- | `border-border` | `--border` | `#EEEEEE` | @deprecated alias — prefer `border-border-subtle` |
454
- | `border-input` | `--input` | `#EEEEEE` | Input field borders |
455
- | `ring-ring` | `--ring` | `#2323FF` | Focus rings |
456
-
457
- > ⚠️ **`border-border` vs `border-border-subtle`:** Both resolve to the same light grey `#EEEEEE`. `border-border` is kept for backward compatibility. **Prefer `border-border-subtle` in all new code.**
458
-
459
- ### Transition tokens
460
-
461
- Duration — primitive scale:
462
-
463
- | Tailwind class | CSS variable | Value | Used for |
464
- |---|---|---|---|
465
- | `duration-instant` | `--primitive-duration-instant` | `50ms` | Focus rings, hover tints |
466
- | `duration-fast` | `--primitive-duration-fast` | `100ms` | Buttons, inputs, badges |
467
- | `duration-base` | `--primitive-duration-base` | `200ms` | Dropdowns, popovers, accordions |
468
- | `duration-slow` | `--primitive-duration-slow` | `300ms` | Dialogs, sheets, drawers closing |
469
- | `duration-open` | `--primitive-duration-open` | `500ms` | Sheets, drawers entering |
470
-
471
- Easing — primitive scale:
88
+ | [AGENTS.md](./AGENTS.md) | **Start here if you're an AI agent** — hard rules, grammar, anti-patterns, component index |
89
+ | [docs/foundations/colors.md](./docs/foundations/colors.md) | Every semantic token: light + dark value and its job |
90
+ | [docs/foundations/typography.md](./docs/foundations/typography.md) | Type scales, the private component-token pattern, weight policy |
91
+ | [docs/foundations/shape-layout.md](./docs/foundations/shape-layout.md) | The 32/40/48 size rail, radius system, spacing, grid |
92
+ | [docs/foundations/motion.md](./docs/foundations/motion.md) | The three-layer motion system: primitives → semantic roles → signature moves |
93
+ | [docs/components/](./docs/components/) | One page per component: API, variants, tokens consumed, behaviour, do/don't |
94
+ | [docs/patterns/](./docs/patterns/) | Forms (Field cascade), menus (shared family), on-brand surfaces & glass |
472
95
 
473
- | Tailwind class | CSS variable | Value | Used for |
474
- |---|---|---|---|
475
- | `ease-standard` | `--primitive-ease-standard` | `cubic-bezier(0.2, 0, 0, 1)` | General UI — bidirectional state changes |
476
- | `ease-enter` | `--primitive-ease-enter` | `cubic-bezier(0, 0, 0.2, 1)` | Overlays / surfaces entering |
477
- | `ease-exit` | `--primitive-ease-exit` | `cubic-bezier(0.4, 0, 1, 1)` | Overlays / surfaces leaving |
478
- | `ease-linear` | `--primitive-ease-linear` | `linear` | Sidebar width, progress bar |
479
-
480
- > Always use token utilities — never raw `duration-200` or `ease-in-out` directly.
481
-
482
- ### Other tokens
483
-
484
- | Tailwind class | Value | Usage |
485
- |---|---|---|
486
- | `bg-slider-range` | blue 40% | Slider filled range |
487
- | `rounded-lg` | `0.625rem` | Base border radius |
488
-
489
- ### Using opacity with tokens
490
-
491
- Opacity on tokens is allowed and encouraged:
492
-
493
- ```
494
- ✅ bg-primary/20 → blue at 20% opacity
495
- ✅ border-border-subtle/50 → light grey border at 50% opacity
496
- ✅ ring-ring/10 → ring at 10% opacity
497
-
498
- ❌ bg-black/50 → NOT a token, use bg-overlay instead
499
- ❌ bg-white → NOT a token, use bg-prominent or bg-card
500
- ```
501
-
502
- ### Primitive alpha scales
503
-
504
- For structured alpha surfaces, the design system ships two internal alpha primitive scales. These are **CSS variables only** — not exposed as Tailwind utility classes. Always reference them via a semantic token, never directly in components.
505
-
506
- ```
507
- --primitive-mono-alpha-4 through --primitive-mono-alpha-80 (black at N%)
508
- --primitive-blue-alpha-4 through --primitive-blue-alpha-80 (brand blue at N%)
509
- Stops: 4 · 8 · 16 · 24 · 32 · 40 · 50 · 64 · 80
510
- ```
511
-
512
- Example — how `--tabs` uses the alpha scale:
513
- ```css
514
- --tabs: var(--primitive-mono-alpha-4); /* black at 4% — tab container background */
515
- ```
516
-
517
- > `--primitive-black-50` is deprecated. It now aliases `--primitive-mono-alpha-50`. The `--overlay` semantic token is unaffected.
518
-
519
- ---
520
-
521
- ## Typography
522
-
523
- The styles export includes pre-built typography classes using **Plus Jakarta Sans**.
524
-
525
- ### Heading scale (Semibold 600 · tracking 1.5px · uppercase)
526
-
527
- | Class | Size / Line height |
528
- |---|---|
529
- | `heading-h1` | 72px / 72px |
530
- | `heading-h2` | 64px / 64px |
531
- | `heading-h3` | 48px / 48px |
532
- | `heading-h4` | 40px / 40px |
533
- | `heading-xs` | 24px / 24px |
534
-
535
- ### Body scale (Semibold 600)
536
-
537
- | Class | Size / Line height |
538
- |---|---|
539
- | `body-lg` | 18px / 28px |
540
- | `body-md` | 16px / 24px |
541
- | `body-sm` | 12px / 16px |
542
- | `body-xs` | 8px / 12px |
543
-
544
- ### Font utilities
545
-
546
- | Tailwind class | Font |
547
- |---|---|
548
- | `font-display` | Plus Jakarta Sans — headings, display text |
549
- | `font-body` or `font-sans` | Plus Jakarta Sans — body text |
550
-
551
- ---
552
-
553
- ## Upgrading the design system
554
-
555
- When you bump `@trading-game/design-intelligence-layer` and run `npm install` (or `npm ci`):
556
-
557
- | How you use the package | What happens |
558
- |-------------------------|----------------|
559
- | You **import** components only from `@trading-game/design-intelligence-layer` | After install and a rebuild, your app uses the **new** implementations in `node_modules/.../dist` — buttons, cards, etc. reflect the version you installed. |
560
- | You **copied** `components/ui/*` (or similar) into your repo | Those files **do not** auto-update. You must delete them and switch to package imports, or manually merge changes from the new package. |
561
-
562
- **Overrides:** Passing large `className` strings onto DS components can mask new defaults (e.g. old radius after a “pill button” update). After upgrading, review those callsites.
563
-
564
- **Cursor / AI agents:** The package ships agent rules at `node_modules/@trading-game/design-intelligence-layer/guides/rules/design-system-consuming-project.mdc`. Cursor does **not** load rules from `node_modules` by default — **re-copy** that file into `.cursor/rules/` after each upgrade so instructions match the release (same command as in **AI Agent Setup** below). The rules include **Rule 7 — Package version upgrades**: agents should search for duplicated components, align with the package, and **tell you explicitly** if local component code was replaced.
565
-
566
- ---
567
-
568
- ## AI Agent Setup
569
-
570
- ### All AI tools (Cursor, Windsurf, Claude Code, Copilot, and others)
571
-
572
- The package ships an `AGENTS.md` file that most AI tools read automatically. After installing the package, copy it to your project root:
96
+ ## Demo app
573
97
 
574
98
  ```bash
575
- cp node_modules/@trading-game/design-intelligence-layer/AGENTS.md ./AGENTS.md
99
+ npm run dev # Next.js demo at http://localhost:4000 — every component, light/dark toggle
576
100
  ```
577
101
 
578
- This tells any AI agent to:
579
- 1. Read the design principles before building any screen
580
- 2. Follow the component and token rules
581
- 3. Run the 7-point checklist before completing any view
582
-
583
- **Design principles file** (bundled in the package):
584
- ```
585
- node_modules/@trading-game/design-intelligence-layer/guides/design-principles/trading-game-design-principles.md
586
- ```
587
-
588
- **Accessibility standards file** (bundled in the package):
589
- ```
590
- node_modules/@trading-game/design-intelligence-layer/guides/accessibility-standards/trading-game-accessibility-standards.md
591
- ```
592
-
593
- **Personas file** (bundled in the package):
594
- ```
595
- node_modules/@trading-game/design-intelligence-layer/guides/personas/trading-game-player-field-guide.md
596
- ```
597
-
598
- **Brand voice** (bundled in the package):
599
- ```
600
- node_modules/@trading-game/design-intelligence-layer/guides/brand-voice/trading-game-brand-voice.md
601
- ```
602
-
603
- All four files apply to all projects built with this package — landing pages, product screens, and games. Every AI agent must read all of them before starting any build. Run the design principles and accessibility checklists before completing any screen. Use the personas and brand voice to guide all player-facing copy.
604
-
605
- ### Cursor
606
-
607
- Copy the included rule file into your project:
102
+ ## Upgrading
608
103
 
609
104
  ```bash
610
- mkdir -p .cursor/rules
611
- cp node_modules/@trading-game/design-intelligence-layer/guides/rules/design-system-consuming-project.mdc .cursor/rules/
612
- ```
613
-
614
- Re-run this **`cp` after every design-system version bump** so your workspace rules stay in sync with the installed package.
615
-
616
- ### Claude Code
617
-
618
- Add the following to your project's `CLAUDE.md`:
619
-
620
- ```markdown
621
- ## Design System
622
-
623
- This project uses @trading-game/design-intelligence-layer. Before writing any UI:
624
- 1. Read node_modules/@trading-game/design-intelligence-layer/guides/design-principles/trading-game-design-principles.md — apply the 8 principles and run the 7-point checklist on every screen
625
- 2. Read node_modules/@trading-game/design-intelligence-layer/guides/accessibility-standards/trading-game-accessibility-standards.md — apply WCAG 2.1 AA standards and run the 9-point accessibility checklist on every screen
626
- 3. Read node_modules/@trading-game/design-intelligence-layer/guides/personas/trading-game-player-field-guide.md — understand the 3 player modes (Edge Seeker, System Runner, Public Predictor) that shape all copy and UX
627
- 4. Read node_modules/@trading-game/design-intelligence-layer/guides/brand-voice/trading-game-brand-voice.md — apply the brand voice: channel-specific voice, banned phrases, vocabulary, and formatting rules for all player-facing copy
628
- 5. Check if the component exists in the package — import it, don't re-implement
629
- 6. Use only design token classes (bg-prominent, text-on-prominent, border-border-subtle, etc.) — no hardcoded hex or raw Tailwind palette colors
630
- 7. Do not install lucide-react, tailwindcss, or other bundled dependencies separately
631
- 8. If no token exists for a value, ask before using a hardcoded value
632
- 9. After upgrading the package: prefer package imports over local copies of components; if replacing local UI code with the package version, tell the user what was overwritten; re-copy guides/rules/design-system-consuming-project.mdc into .cursor/rules if using Cursor
633
-
634
- See node_modules/@trading-game/design-intelligence-layer/guides/rules/design-system-consuming-project.mdc for full rules.
635
- See node_modules/@trading-game/design-intelligence-layer/README.md for complete token and component reference.
636
- ```
637
-
638
- ---
639
-
640
- ## Common mistakes
641
-
642
- | Wrong | Right | Why |
643
- |---|---|---|
644
- | `bg-gray-100` | `bg-subtle` | Raw Tailwind palette — use tokens |
645
- | `bg-white` | `bg-prominent` or `bg-card` | Not a semantic token |
646
- | `text-white` | `text-on-<surface>` (e.g. `text-on-primary`, `text-on-prominent-inverse`, `text-on-semantic-win`) | Pick the paired foreground for the actual background |
647
- | `text-black` | `text-on-prominent` | Not a semantic token |
648
- | `text-gray-400` | `text-on-disabled` | Use the disabled text token |
649
- | `bg-black/50` | `bg-overlay` | Overlay has its own token |
650
- | `bg-[#2323FF]` | `bg-primary` | Hardcoded hex — use token |
651
- | `border-border` | `border-border-subtle` | Old name — prefer the new explicit name |
652
- | `bg-[var(--primary)]` | `bg-primary` | Raw CSS var — Tailwind v4 maps tokens directly |
653
- | `hsl(var(--primary))` | `bg-primary` | Tailwind v3 syntax — not needed in v4 |
654
- | Installing `lucide-react` | Already bundled | Icons are included in the package |
655
-
656
- ---
657
-
658
- ## Do NOT install separately
659
-
660
- These are bundled with the package. Installing them separately can cause version conflicts:
661
-
662
- - `lucide-react` — icon library
663
- - `radix-ui` — headless primitives
664
- - `class-variance-authority` — variant API
665
- - `cmdk` — command palette
666
- - `vaul` — drawer
667
- - `sonner` — toast
668
- - `recharts` — charts
669
- - `react-day-picker` — calendar
670
- - `embla-carousel-react` — carousel
671
- - `react-resizable-panels` — resizable panels
672
-
673
- ---
674
-
675
- ## Development (contributors only)
676
-
677
- ```bash
678
- # Clone
679
- git clone https://github.com/trading-game/TG-design-system.git
680
- cd TG-design-system
681
-
682
- # Install dependencies
683
- npm install
684
-
685
- # Start dev server (Next.js component playground)
686
- npm run dev
687
-
688
- # Build the library (ESM + CJS + types)
689
- npm run build
690
-
691
- # Build the Next.js showcase app
692
- npm run build:next
105
+ npm install @trading-game/design-intelligence-layer@latest --safe-chain-skip-minimum-package-age
693
106
  ```
694
107
 
695
- ### Updating design tokens
696
-
697
- Design tokens are managed in Figma and exported as CSS variables. To update:
698
-
699
- 1. Update the CSS custom properties in `app/globals.css` (playground) and `src/styles.css` (published package)
700
- 2. Update `guides/rules/design-system-consuming-project.mdc` to reflect any token renames
701
- 3. Update this README's token tables
702
-
703
- ---
704
-
705
- ## Tech stack
706
-
707
- - **React 19** + **TypeScript**
708
- - **Tailwind CSS v4** — CSS-first configuration
709
- - **shadcn/ui** (New York style) — base component primitives
710
- - **Radix UI** — accessible headless primitives
711
- - **Figma** — source of truth for design tokens
712
-
713
- ---
714
-
715
- ## Changelog
716
-
717
- ### Unreleased
718
- - **Button — new `tertiary-inverse` variant:** mirror of `tertiary` (transparent + text-only) but recoloured for use on dark / brand-coloured surfaces — `bg-transparent text-on-primary hover:bg-on-primary/[0.10]`. Pair with any size including `icon-*` (icon-only square). Same `variant` × `size` orthogonality as every other Button variant.
719
- - **Input — new `variant="inverse"`:** sunken dark fill + white text recipe for use on `bg-primary` (and other coloured surfaces) where the default frosted-white fill blends into the chrome. 25 %-white border, `bg-input-inverse-surface` fill (35 % black), white text, 45 %-white placeholder, white selection. On focus the border solidifies to 100 % white with a 40 %-white ring. `variant="default"` (the existing look) is unchanged — no existing call site moves visually.
720
- - **InputGroup — new `variant="inverse"`:** mirrors the Input variant. A new `InputGroupVariantContext` propagates the variant down so `InputGroupInput`, `InputGroupAddon`, and `InputGroupText` all pick up the inverse styling automatically (addon icons / suffix text swap to `text-on-primary/70`). Override per-instance via `className` (e.g. `border-transparent` for the borderless wallet-address treatment).
721
- - **New token — `--input-inverse-surface`:** added in both `src/styles.css` and `app/globals.css` (component-token layer, references the existing primitive palette). Mapped via `@theme inline` to `--color-input-inverse-surface` so it's exposed as `bg-input-inverse-surface` to consumers. Tune the dark-fill shade once, propagates everywhere.
722
- - **Form-control consistency pass — radius unified to `rounded-xs` (4 px):** `Input`, `InputGroup`, and `Select` (trigger) moved from `rounded-sm` (6 px) → `rounded-xs` (4 px), matching `Button` across every size. Form controls placed in a row (`<Input>` + `<Select>` + `<Button>`) now share identical heights *and* identical corner radii.
723
- - **Form-control consistency pass — resting shadows removed:** `Input` and `InputGroup` dropped their `shadow-xs` resting drop-shadow so they sit flat alongside the shadowless `Select` and `Button`. Overlay shadows (Dialog, Popover, Tooltip, etc.) are untouched — only the skeuomorphic raised-surface shadow on form inputs is gone.
724
- - **InputGroup — addon-side padding no longer mirrors:** previously, an inline-end addon would also reduce the input's *left* padding to `pl-2` "for visual symmetry," squeezing text to the field's left edge. Now only the side an addon is attached to reduces its inset; the opposite side keeps the natural Input `pl-3` / `pr-3` (12 px). Fixes the suffix-text input ("0.00 ... USD") looking flush-left compared to the prefix-icon row.
725
- - **Component playground — Button section split into two columns:** the showcase Button page now renders default-surface variants on the left and `bg-primary`-surface inverse variants on the right, side-by-side, with row-by-row alignment so each (regular ↔ inverse) pair sits on the same y-line. Stacks vertically below `lg:` (1024 px) with the inverse half dropping below the default half. Inverse rows previously sat in individual `bg-primary` wrappers — now a single shared blue panel.
726
- - **Component playground — Input / InputGroup inverse showcases:** both sections gain a `variant="inverse"` row demonstrating the new variant on `bg-primary`. Plus a standalone `/input-on-blue` route with longer-form exploration (default vs inverse; borderless wallet-address mock; real-world sign-in row).
727
- - **Component playground — `/tertiary-inverse` route:** standalone page demonstrating the new Button `tertiary-inverse` variant at every `icon-*` size and labeled size on a brand-blue surface.
728
- - **OpenPositionsBlock — double-divider fix:** the block was rendering both its own manual `<div className="h-px ...">` between rows AND the new ItemGroup `::after` auto-divider, producing two hairlines per gap. The manual divs are removed; the ItemGroup auto-divider now handles spacing. _Migration note for consumers:_ if you wrap your own `<Item>` rows in an `<ItemGroup>` and were manually injecting dividers between them, drop the manual dividers — or pass `divider={false}` on the `<ItemGroup>` to opt out and keep your own. The same cleanup landed in the demo app's Sheet "List" showcase.
729
- - **Button — two new shimmer variants for headline CTAs:** `primary-shimmer` and `primary-inverse-shimmer`. Same surface tokens as their non-shimmer cousins, plus `relative overflow-hidden` to anchor an absolutely-positioned shimmer overlay. The overlay sweeps a diagonal (`110deg`) gradient driven by a new `button-shimmer` keyframe — two quick back-to-back sweeps every cycle, then a ~3 s pause off-screen, so it reads as an occasional glint rather than a continuous loader. Peak opacity is tuned per-surface (white at 32 % over brand blue; brand-blue at 22 % over white) and falls off into transparent edges. Suppressed automatically when the button is `loading` or `disabled`.
730
- - **Button — `button-shimmer` keyframe:** new `@keyframes button-shimmer` in both `src/styles.css` and `app/globals.css`. Pattern: `0%, 10% → 30%, 100%` translateX, with an opacity fade at the seam between the two sweeps so the reset between flashes is invisible.
731
- - **Badge — `fill-credit` is now a frosted glass chip:** `bg-semantic-boost-surface` was repurposed from the old peach (`#FDCA8A`) to `oklch(1 0 0 / 0.22)` — a translucent white surface that composes over whatever sits behind it. Paired with `backdrop-blur-md` and `text-on-primary` (white). Custom `!font-bold` / `!text-xs` overrides removed — the variant now only sets surface tokens, so font weight / size match every other badge (`font-semibold`, `text-xs` from the base). Border override removed too. The variant is intentionally invisible on white surfaces — designed to live on coloured surfaces (e.g. a brand-blue hero card / wallet card), matching the wallet mock's `.c-hero-wc`.
732
- - **Item — auto-divider in `ItemGroup`:** new `divider` prop (default `true`) adds a 1 px `bg-border-subtle` divider between consecutive default-variant `Item`s. The line is drawn via `::after` (not a border on the item itself), sitting in a 4 px `gap-1` between items with `inset-x-4` so it aligns with the item content padding. The last item's `::after` is suppressed; non-default variants (`outline`, `muted`) are untouched. Pass `divider={false}` to opt out for flush items or manual `<ItemSeparator />` usage.
733
- - **Tooltip — black surface by default:** `variant="default"` switched from brand-blue to black (`bg-prominent-inverse` + `text-on-prominent-inverse`). The arrow follows. `variant="inverse"` (white) is unchanged. Default primitive now bakes in `max-w-xs whitespace-normal`, so consumers no longer pass these classes.
734
- - **Tooltip — keyboard-shortcut breathing room:** when the tooltip contains a `Kbd` (detected via `:has([data-slot=kbd])`), padding bumps to `px-3.5 py-2` and child elements get `space-y-1.5` so the title / kbd-row pair doesn't crowd.
735
- - **Switch — pill + white circle knob:** track switched from `rounded-[6px]` to `rounded-full`, thumb from `rounded-[3px]` to `rounded-full`. Thumb is always `bg-prominent` (white) in both states. Checked track: `bg-primary`. Unchecked track: `bg-input`. No API change.
736
- - **Link — underline always visible:** swapped `hover:underline` → `underline` so the underline reads at rest, not only on hover.
737
- - **Sidebar — primary-tinted selection + 16 px padding rails:** active menu items now use `bg-primary/10` (brand-blue tint) instead of `bg-secondary-hover` (grey). `SidebarHeader` / `SidebarFooter` / `SidebarGroup` use `px-layout-margin-inline py-2` (16 px horizontal) when expanded; the icon-collapsed rail falls back to `px-2` so the icons still centre. The `lg`-size menu button gets `p-0! justify-center` when collapsed so a 32 px brand block fills the rail while a 16 px lucide icon still centres in the same column.
738
- - **Banner — close X corner anchor:** the close `X` stays grid-centred on single-line banners but pins to `absolute top-3 right-3` whenever `description` is set or `layout="card"` — so it reads as a true corner dismiss instead of floating mid-body.
739
- - **Button — instant bg-color hover:** the button base now uses `transition-[color,border-color,opacity,box-shadow,transform]` instead of `transition-all`. `background-color` flips instantly on hover so the brief partially-transparent fade phase (which let surrounding surface tints bleed through a hovered secondary button) is gone.
740
-
741
- ### v0.16.7
742
- - **Credit badge readability pass:** `<Badge variant="fill-credit">` and the credit pill inside `CreditTicketCard` now sit on a deeper peach surface and use a bold deep-amber text for crisper read on the grey `bg-subtle` ticket card and on white / brand-blue surfaces alike. Token chain: `--primitive-amber-100` (#FEEBD9) → `--primitive-amber-300` (#FDCA8A) and `--primitive-amber-800` (#713813) → `--primitive-amber-900` (#521900). `--semantic-boost-surface` and `--on-semantic-boost` are repointed automatically. Variant class also bumps to `!font-bold`.
743
- - **CreditTicketCard — responsive compact layout:** Compact variant now keeps the credit badge inline next to the value when there's room and only wraps to a second line when balance + badge would overflow into the deposit stub (`flex-wrap` on the value/badge row, with `whitespace-nowrap` on the value so the number itself never splits). Non-compact keeps the existing inline behavior.
744
- - **Banner — outer corner radius unified with Card / Alert:** outer surface `rounded-md` (8 px) → `rounded-sm` (6 px). Brand `IconBadge` reverted to `rounded-xs` (4 px square) after a brief detour through `rounded-full` — the square reads better against the surface.
745
- - **Banner — multi-line icon top-alignment:** when a description is present (or `layout="card"`), the grid switches from `items-center` to `items-start` so the leading icon aligns with the title's top instead of sliding to the middle of the body block as it wraps.
746
- - **Banner — class-order fix for `pr-12`:** in the card branch, `pr-12` was being merged before `SURFACE_CLASSES`'s `px-4`, so tailwind-merge kept the smaller right padding and long descriptions wrapped under the absolute-positioned close X. Re-ordering the `cn()` arguments puts `pr-12` after `SURFACE_CLASSES` so it wins (48 px right padding).
747
- - **Alert — card layout body/close cell overlap fixed:** card body changed from `col-span-2 col-start-2` to `col-start-2 min-w-0`. Previously the body spanned cols 2-3 (same cell as the close X), so long descriptions slid under the close. Now the body lives in col 2 only and the close owns col 3.
748
- - **Tooltip — `max-w-xs` + `whitespace-normal` built into the primitive:** Desktop tooltips now cap at 320 px by default with natural-word wrapping; consumers no longer need to pass these classes. Mobile drawers (the responsive Tooltip mode) keep the full sheet width — the consumer `className` is no longer applied to the drawer's inner content wrapper, so passing `max-w-xs` on `TooltipContent` for desktop tuning no longer leaks into and shrinks the mobile drawer text.
749
- - **Naming note:** the v0.14.0 entry below claimed a `fill-credit` → `fill-boost` and `CreditTicketCard` → `BoostTicketCard` rename. **Those renames were not landed** — the current shipped names are still `fill-credit`, `CreditTicketCard`, and `--semantic-boost-surface` / `--on-semantic-boost` (the boost tokens stayed, the variant/component names didn't get renamed alongside them).
750
-
751
- ### v0.16.5 — v0.16.6
752
- - **New `Chip` component:** Pill-shaped toggle-able selection chip — `bg-primary/[0.08]` + `text-primary` when selected, outline + `text-on-prominent` when unselected. Used for game filters (All games, Rise & Fall, Swipe, Box-O, Digits) and similar mutually-exclusive selectors.
753
- - **FAQ block — two-column layout:** `FAQBlock` gained a two-column variant on desktop.
754
- - **Demo + block logos** repointed at the new `.webp` brand assets (`/assets/icon.webp`, `/assets/tg-logo-full.webp`).
755
- - **Open Positions block** — "View transaction history" footer link renamed to "View trade history".
756
-
757
- ### v0.16.1 — v0.16.4
758
- - **New `Banner` component:** Promotional surface (separate from `Alert`, which is now status-only). Default variant places a brand-blue 40 px circular `IconBadge` (consumer-swappable via `glyph`) on a tinted primary surface. Layouts: `inline`, `card`. Slots: `title`, `description`, `icon`/`glyph`, `action`, `actions`, `onDismiss`, `onClick` (whole-banner clickable).
759
- - **`fill-demo` Badge variant:** Opaque peach surface (color-mix of demo @ 10% with prominent) + red-orange `--semantic-demo` text — for demo / test-mode account flags. Works identically on any background.
760
- - **Alert slimmed to status-only:** `default` and `brand` variants removed — use `Banner` for promotional / marketing surfaces.
761
-
762
- ### v0.14.0
763
- - **New Result block:** Fixed-width (320 px) end-of-round result card with 5 variants — Win/continuing, Loss/continuing, End of demo, Out of balance, On a roll. Thumb-up/down SVG with subtle radial halo + idle motion (float for win, head-shake for loss). Title (`You won!` / `You lost!`), amount headline in semantic colour, contract + duration `standard` Badge pills, and CTA(s). Digit contracts render the picked digit inside the contract pill with a thin vertical divider. Out-of-balance and on-a-roll variants are dialog-only (no thumb / no amount) — title + body + 2 CTAs. Lives under **Blocks → Result** in the playground.
764
- - **Sheet Open Positions redesign:** Each row now uses a 2-col layout — bet name + stake (left) / PnL + market·duration (right). Dropped per-row asset (`V100`), round number, and digit/multiplier suffix labels (the game is already implied by the sheet/tab title). Abbreviated durations (sec / min / hr / day) and dropped the `Stake ` prefix on amounts. Added an "Edge cases" row demonstrating drawer behavior with 1 item and 30 items.
765
- - **Section nav sheet — desktop-safe:** The mobile section picker `Sheet` is now conditionally mounted (`{isMobile && …}`) so it can't be triggered at desktop widths where the permanent sidebar is already visible, avoiding redundant overlay layers.
766
- - **Badge — new `standard` variant:** Neutral grey chip (`bg-subtle` / `text-on-prominent`) for non-status meta like contract type and duration. Existing `default` (solid blue) and all other variants are unchanged.
767
- - **TicketCard / BoostTicketCard:**
768
- - Left content restructured as a 2-column grid so the icon circle scales to match the **label + value height** (was a fixed `size-10` centered against the entire column).
769
- - Boost badge now sits in row 2 of column 2 — aligned under the value, not pinned to the card's left edge.
770
- - **2-decimal balance formatting:** the `value` prop is now normalised by `formatBalanceValue` before render, so values always display with exactly two decimal places regardless of input (e.g. `$12,450` → `$12,450.00`, `$12,450.5` → `$12,450.50`, `1234` → `1,234.00`). Currency prefix/suffix is preserved.
771
- - **Token convention — `on-<surface>` foreground pairs:** Added a Material-style paired foreground for every semantic surface so colour roles are self-documenting and resolve robustly across consumer setups (incl. Module Federation, where raw `var()` fallbacks were previously needed). New CSS variables and Tailwind utilities:
772
- - `--on-primary` (white) / `text-on-primary` — paired with `bg-primary`
773
- - `--primary-inverse` (white) / `bg-primary-inverse` — inverted primary surface for use on dark/coloured areas
774
- - `--on-primary-inverse` (blue) / `text-on-primary-inverse` — paired with `bg-primary-inverse`
775
- - `--prominent-inverse` (black) / `bg-prominent-inverse` — dark surface for use on light pages (e.g. toasts)
776
- - `--on-prominent-inverse` (white) / `text-on-prominent-inverse` — paired with `bg-prominent-inverse`
777
- - `--on-semantic-win` / `--on-semantic-loss` / `--on-semantic-warning` (all white) and `--on-semantic-boost` (dark amber) — paired with the matching `bg-semantic-*` surfaces.
778
- - **Internal sweep:** all internal components (Button, Badge, Tooltip, Toast, Checkbox, Avatar, Input, NativeSelect, TicketCard, BoostTicketCard, sidebar mini-avatar) now use the explicit `on-<surface>` foregrounds — no more catch-all `text-on-prominent-static-inverse`.
779
- - **Backwards-compat aliases preserved:** `--on-prominent-static-inverse` (→ `var(--on-prominent-inverse)`) and `--surface-inverse-static` (→ `var(--primary-inverse)`) remain defined in CSS so existing consumer code referencing them via raw `var()` (e.g. `var(--on-prominent-static-inverse, #fff)`) keeps working. The Tailwind utility `text-on-prominent-static-inverse` is also still emitted, but new code should pick the explicit paired token.
780
- - **Sonner → Toast rename:** The toast wrapper file moved from `components/ui/sonner.tsx` to `components/ui/toast.tsx`. The wrapper component is still `Toaster`, and `toast` is now re-exported from this module so consumers no longer need `import { toast } from "sonner"` directly. Import path: `@trading-game/design-intelligence-layer` (was `... from "sonner"`).
781
- - **Boost token rename (kept):** `--semantic-credit` → `--semantic-boost` and `--semantic-credit-text` → `--on-semantic-boost`. The boost surface and on-boost text tokens are the current shipped names. **Breaking change** for consumers that referenced the legacy `--semantic-credit*` names directly.
782
- - **~~`fill-credit` → `fill-boost` Badge variant rename~~ — _not landed_.** The Badge variant for credit/bonus labels is still `fill-credit` (see current v0.16.7 entry above). The intended rename was rolled back during release.
783
- - **~~`CreditTicketCard` → `BoostTicketCard` rename~~ — _not landed_.** The component is still `CreditTicketCard`. Props remain `badgeIcon`, `badgeLabel`, `badgeValue`, `badgeCurrency`, etc. (see current entry above).
784
- - **Button `primary-inverse` / `secondary-inverse`:** Now built from the new `bg-primary-inverse` / `text-on-primary-inverse` / `border-primary-inverse` / `text-on-prominent-inverse` tokens. Visually identical to before, but the underlying class names are different.
785
- - **Toast — action button visual fix:** The toast action button (the inverse-coloured "Undo" pill) now uses `primary-inverse` colouring (white bg + blue text) to match the dark toast surface, instead of the previous `bg-primary` (blue on dark — low hierarchy / wrong surface-context).
786
-
787
- ### v0.12.0
788
- - **New semantic tokens — credit/boost:** `--semantic-boost` (`#F9840F` amber-orange) and `--on-semantic-boost` (`#521900` deep amber, updated in v0.16.7 — was `#713813`) live in both `src/styles.css` and `app/globals.css`. Tailwind utilities: `bg-semantic-boost`, `text-on-semantic-boost`, plus `bg-semantic-boost-surface` for the tinted pill background.
789
- - **Badge — `fill-credit` variant:** Variant for credit/bonus labels — `bg-semantic-boost-surface` background with `text-on-semantic-boost` foreground. Pair with a `Gift` or `TicketPlus` icon. Badge base class no longer applies `tracking-wide`; letter spacing is default on all badge variants. _(Visual updated in v0.16.7 — see entry above.)_
790
- - **`CreditTicketCard` component:** Exported alongside `TicketCard`. Renders the same ticket shell (notches, tear-line, shimmer stub) but with a variable-height card body and an inline `<Badge variant="fill-credit">` next to or below the value for credit/bonus display. Props include `icon`, `label`, `value`, `currency`, `stubLabel`, `onStubClick`, `stubDisabled`, `badgeIcon`, `badgeLabel`, `badgeValue`, `badgeCurrency`, `badgeInfo`, `compact`.
791
- - **`TicketCard` — `compact` prop:** New boolean prop (default `false`). When `true`, reduces left-content padding from `px-4 gap-4` to `px-2 gap-2` for mobile layouts.
792
- - **`TicketCard` — welcome-credit props removed:** `welcomeCredit`, `freeCredit` (deprecated alias), `creditUsed`, `creditTotal`, `creditCurrency`, `creditLabel`, `creditFullWidth`, and the `Progress`-based credit bar have been removed. Use `CreditTicketCard` for the credit use case.
793
- - **`tracking-wide` / `tracking-widest` removed from several components:** Badge base class, `Link` base class, `PaginationLink`, `CommandShortcut`, `ContextMenuShortcut`, `DropdownMenuShortcut`, and `MenubarShortcut` no longer apply letter-spacing overrides. All render with default tracking.
794
-
795
- ### v0.10.1
796
- - **TicketCard — Wallet icon:** Default icon in the circular badge is now a `Wallet` icon (replaces `CircleDollarSign`).
797
- - **TicketCard — sentence case labels:** The label above the value (e.g. "Total balance") and the stub label (e.g. "Deposit") no longer apply `uppercase` or `tracking-widest`. Both render in sentence case with default letter spacing.
798
- - **Select — sm / md / lg sizes:** `SelectTrigger` now accepts `size="sm"` (h-8, text-xs) | `"md"` (h-10, text-sm, **default**) | `"lg"` (h-12, text-base), matching the `Input` and `InputGroup` height scale. The previous `"default"` size alias is replaced by `"md"`.
799
-
800
- ### v0.10.0
801
- - **Demo app — responsive mobile layout:** The component playground is now fully mobile-friendly. Foundations / Components / Blocks navigation moves to a fixed bottom tab bar on mobile. A hamburger menu opens a slide-in Sheet to pick any component within the active tab. The desktop sidebar and inline tab switcher are unchanged.
802
- - **Sonner toast — mobile-aware behaviour:** Toasts now appear at `top-center` on mobile (single visible toast) and `bottom-right` on desktop (up to 3). Added custom token-based styling (`--on-prominent` background, inverse text, `font-display` title).
803
- - **Toaster registered in root layout:** `<Toaster />` is now mounted in `app/layout.tsx` so toast notifications work globally.
804
- - **InputOTP docs:** README and design-system guide updated with full variant listing — OTP 4 digits, OTP 6 digits, OTP 6 split, OTP 8 digits, OTP 6 separated.
108
+ Deprecated token aliases and component aliases (e.g. Input `variant="inverse"`, NativeSelect `size="default"`) keep working until **v1.0**, when they are removed in one breaking sweep alongside the Champion Trade package rename.