@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
@@ -0,0 +1,85 @@
1
+ # Accordion
2
+
3
+ Vertically stacked disclosure sections that expand one panel of content at a time.
4
+
5
+ ## When to use
6
+
7
+ - Progressive disclosure of secondary content — FAQs, settings groups, order details — where the page should stay short.
8
+ - Content the user scans by heading first and opens selectively.
9
+
10
+ **When not:** For switching between peer views of equal importance use Tabs. For a single modal confirmation use [Dialog](./dialog.md).
11
+
12
+ ## Anatomy
13
+
14
+ - `Accordion` — Radix root, pass-through; `data-slot="accordion"`.
15
+ - `AccordionItem` — one section; bottom hairline `border-border-default-default`, removed on the last item; `data-slot="accordion-item"`.
16
+ - `AccordionTrigger` — the heading row inside a Radix `Header`; chevron rotates 180° when open; `data-slot="accordion-trigger"`.
17
+ - `AccordionContent` — animated panel (`animate-accordion-down/up`), inner wrapper carries `pb-4`; `data-slot="accordion-content"`.
18
+
19
+ ## API
20
+
21
+ ### Accordion
22
+
23
+ | Prop | Type | Default | Notes |
24
+ | --- | --- | --- | --- |
25
+ | `type` | `"single" \| "multiple"` | — | Radix prop; required. |
26
+ | `collapsible` | `boolean` | `false` | With `type="single"`, allows closing the open item. |
27
+ | `value` / `defaultValue` | `string \| string[]` | — | Controlled / uncontrolled open state. |
28
+ | `onValueChange` | `(value) => void` | — | Radix change callback. |
29
+
30
+ ### AccordionItem
31
+
32
+ | Prop | Type | Default | Notes |
33
+ | --- | --- | --- | --- |
34
+ | `value` | `string` | — | Required item identity. |
35
+
36
+ `AccordionTrigger` and `AccordionContent` take standard Radix props plus `className`.
37
+
38
+ ## Variants & sizes
39
+
40
+ One variant, no size prop.
41
+
42
+ ```tsx
43
+ import {
44
+ Accordion,
45
+ AccordionItem,
46
+ AccordionTrigger,
47
+ AccordionContent,
48
+ } from "@trading-game/design-intelligence-layer"
49
+
50
+ <Accordion type="single" collapsible>
51
+ <AccordionItem value="fees">
52
+ <AccordionTrigger>What are the fees?</AccordionTrigger>
53
+ <AccordionContent>No commission on demo trades.</AccordionContent>
54
+ </AccordionItem>
55
+ </Accordion>
56
+ ```
57
+
58
+ ## Tokens
59
+
60
+ **Colour (semantic):** `border-border-default-default` (item hairline), `icon-prominent-default` (chevron), `ring-focus-strong` (focus ring).
61
+ **Type (private):**
62
+
63
+ | Token | Value |
64
+ | --- | --- |
65
+ | `--accordion-trigger-*` | 14 / medium (500) / 20 |
66
+ | `--accordion-content-*` | 14 / regular (400) / 20 |
67
+
68
+ ## Behaviour
69
+
70
+ - Open state is controlled via Radix `value`/`onValueChange`; `type="single"` closes siblings automatically.
71
+ - The trigger is `rounded-md`, underlines on hover, and takes the standard control focus ring (`focus-visible:ring-[3px] ring-ring-focus-strong`).
72
+ - Disabled triggers get `pointer-events-none opacity-24`.
73
+ - Expand/collapse uses the `accordion-down`/`accordion-up` keyframes; the chevron transition is 200 ms.
74
+
75
+ ## Do / Don't
76
+
77
+ **Do**
78
+ - Keep trigger copy to a single line at 14/medium/20 — the `.type-accordion-trigger` class wins over `font-*` utilities.
79
+ - Rely on the item's built-in hairline; do not add your own dividers.
80
+ - Use `type="single" collapsible` for FAQ-style lists.
81
+
82
+ **Don't**
83
+ - Don't bold the trigger — medium (500) is the trigger weight; semibold 600 is the system maximum anywhere.
84
+ - Don't add shadows or background fills to items — accordion rows sit flat on the page surface.
85
+ - Don't nest interactive controls inside `AccordionTrigger`; it is already a button.
@@ -0,0 +1,98 @@
1
+ # Alert Dialog
2
+
3
+ Interrupting modal that demands an explicit decision — confirm or cancel — before anything else can happen.
4
+
5
+ ## When to use
6
+
7
+ - Destructive or irreversible actions: closing a position, deleting a watchlist, discarding an unsaved order.
8
+ - Any flow where dismissing by clicking the overlay would be dangerous — the alert dialog has no close X and no overlay dismiss.
9
+
10
+ **When not:** For informational or form-bearing modals that may be freely dismissed use [Dialog](./dialog.md). For mobile flows that slide from an edge use [Drawer](./drawer.md).
11
+
12
+ ## Anatomy
13
+
14
+ - `AlertDialog` — Radix root; `data-slot="alert-dialog"`.
15
+ - `AlertDialogTrigger` / `AlertDialogPortal` / `AlertDialogOverlay` — plumbing; overlay paints `background-overlay-default`; `data-slot` on each.
16
+ - `AlertDialogContent` — the centred card shell, `rounded-2xl` (18px), no shadow; `data-slot="alert-dialog-content"`, `data-size`.
17
+ - `AlertDialogHeader` — grid that centres content (or left-aligns at `sm` for `size="default"`); `data-slot="alert-dialog-header"`.
18
+ - `AlertDialogMedia` — optional 64px icon square (`bg-background-secondary-surface`, `rounded-md`); `data-slot="alert-dialog-media"`.
19
+ - `AlertDialogTitle` / `AlertDialogDescription` — typed via `type-alert-dialog-title` / `-description`.
20
+ - `AlertDialogFooter` — stacked buttons, row at `sm`; two-column grid in `size="sm"`; `data-slot="alert-dialog-footer"`.
21
+ - `AlertDialogAction` / `AlertDialogCancel` — Radix actions rendered through `Button` via `asChild`.
22
+
23
+ ## API
24
+
25
+ ### AlertDialogContent
26
+
27
+ | Prop | Type | Default | Notes |
28
+ | --- | --- | --- | --- |
29
+ | `size` | `"default" \| "sm"` | `"default"` | `sm` = `max-w-xs`, centred copy, two-up full-width footer buttons; `default` = `sm:max-w-lg`. |
30
+
31
+ ### AlertDialogAction / AlertDialogCancel
32
+
33
+ | Prop | Type | Default | Notes |
34
+ | --- | --- | --- | --- |
35
+ | `variant` | Button variant | `"primary"` (Action) / `"tertiary"` (Cancel) | Any [Button](./button.md) variant. |
36
+ | `size` | Button size | `"md"` | 40px rail default. |
37
+
38
+ `AlertDialog` root takes Radix `open` / `defaultOpen` / `onOpenChange`.
39
+
40
+ ## Variants & sizes
41
+
42
+ ```tsx
43
+ import {
44
+ AlertDialog,
45
+ AlertDialogTrigger,
46
+ AlertDialogContent,
47
+ AlertDialogHeader,
48
+ AlertDialogTitle,
49
+ AlertDialogDescription,
50
+ AlertDialogFooter,
51
+ AlertDialogAction,
52
+ AlertDialogCancel,
53
+ } from "@trading-game/design-intelligence-layer"
54
+
55
+ <AlertDialog>
56
+ <AlertDialogTrigger>Close position</AlertDialogTrigger>
57
+ <AlertDialogContent size="sm">
58
+ <AlertDialogHeader>
59
+ <AlertDialogTitle>Close this position?</AlertDialogTitle>
60
+ <AlertDialogDescription>This cannot be undone.</AlertDialogDescription>
61
+ </AlertDialogHeader>
62
+ <AlertDialogFooter>
63
+ <AlertDialogCancel>Keep it</AlertDialogCancel>
64
+ <AlertDialogAction>Close position</AlertDialogAction>
65
+ </AlertDialogFooter>
66
+ </AlertDialogContent>
67
+ </AlertDialog>
68
+ ```
69
+
70
+ ## Tokens
71
+
72
+ **Colour (semantic):** `background-overlay-default` (scrim), `background-primary-surface` (card), `border-border-default-default` (hairline), `text-text-subtle-default` (description), `background-secondary-surface` (media square); action/cancel inherit Button tokens.
73
+ **Type (private):**
74
+
75
+ | Token | Value |
76
+ | --- | --- |
77
+ | `--alert-dialog-title-*` | 18 / semibold (600) / 24 |
78
+ | `--alert-dialog-description-*` | 14 / regular (400) / 20 |
79
+
80
+ ## Behaviour
81
+
82
+ - Content is `p-6`, `gap-6`, `max-w-[calc(100%-2rem)]`, centred with translate; open/close animates fade + 95% zoom.
83
+ - There is deliberately no close X — the only exits are Action and Cancel.
84
+ - `AlertDialogMedia` shifts the header into a media grid; at `sm` viewport with `size="default"` the title moves to column 2 beside the media square.
85
+ - `size="sm"` forces both footer buttons full-width in a 2-column grid.
86
+ - Action and Cancel are real Buttons (`asChild` onto the Radix primitives), so `loading`/`shimmer` are unavailable on them.
87
+
88
+ ## Do / Don't
89
+
90
+ **Do**
91
+ - Keep the title at 18/semibold/24 — semibold 600 is the system maximum weight.
92
+ - Use `size="sm"` for one-line confirmations; `default` for anything with media or longer copy.
93
+ - Make the destructive verb the Action label ("Close position", not "OK").
94
+
95
+ **Don't**
96
+ - Don't add a shadow or a close X — the shell is flat and non-dismissable by design.
97
+ - Don't override the `rounded-2xl` 18px radius; dialogs and cards share it.
98
+ - Don't reach for AlertDialog for routine content — it traps focus and blocks the page.
@@ -0,0 +1,55 @@
1
+ # Aspect Ratio
2
+
3
+ Constrains its child to a fixed width-to-height ratio regardless of container width.
4
+
5
+ ## When to use
6
+
7
+ - Game artwork, chart thumbnails, video embeds, or any media that must hold 16/9, 4/3, or 1/1 while the layout flexes.
8
+
9
+ **When not:** For circular imagery of a person use [Avatar](./avatar.md). If the media already has intrinsic dimensions and can reflow freely, plain `max-width:100%` is enough.
10
+
11
+ ## Anatomy
12
+
13
+ - `AspectRatio` — the only export; a direct pass-through to the Radix primitive with `data-slot="aspect-ratio"`. No styling of its own.
14
+
15
+ ## API
16
+
17
+ ### AspectRatio
18
+
19
+ | Prop | Type | Default | Notes |
20
+ | --- | --- | --- | --- |
21
+ | `ratio` | `number` | `1` | Width / height, e.g. `16 / 9`. |
22
+
23
+ All other props pass through to the underlying `div`.
24
+
25
+ ## Variants & sizes
26
+
27
+ No variants or sizes — the ratio is the only knob.
28
+
29
+ ```tsx
30
+ import { AspectRatio } from "@trading-game/design-intelligence-layer"
31
+
32
+ <AspectRatio ratio={16 / 9} className="overflow-hidden rounded-2xl">
33
+ <img src="/art/crash-game.png" alt="Crash game" className="size-full object-cover" />
34
+ </AspectRatio>
35
+ ```
36
+
37
+ ## Tokens
38
+
39
+ **Colour (semantic):** none — the component paints nothing.
40
+ **Type (private):** none.
41
+
42
+ ## Behaviour
43
+
44
+ - Purely presentational; the child is absolutely positioned by Radix to fill the ratio box.
45
+ - Rounding, borders, and `object-cover` are the consumer's responsibility via `className` on the wrapper or child.
46
+
47
+ ## Do / Don't
48
+
49
+ **Do**
50
+ - Pair with `overflow-hidden` and a system radius (`rounded-2xl` for card-like media) when clipping imagery.
51
+ - Give the child `size-full object-cover` so it fills the box.
52
+
53
+ **Don't**
54
+ - Don't fix a pixel height on the wrapper — that defeats the ratio.
55
+ - Don't add shadows to media frames; surfaces stay flat system-wide.
@@ -0,0 +1,88 @@
1
+ # Avatar
2
+
3
+ Circular representation of a user — image with initials fallback, optional status badge, and stacked groups.
4
+
5
+ ## When to use
6
+
7
+ - Account entry points, leaderboards, comments, or anywhere a person needs a compact visual identity.
8
+
9
+ **When not:** For arbitrary media at a fixed ratio use [Aspect Ratio](./aspect-ratio.md). For status or counts on their own use [Badge](./badge.md).
10
+
11
+ ## Anatomy
12
+
13
+ - `Avatar` — circular root with size states; `data-slot="avatar"`, `data-size`.
14
+ - `AvatarImage` — the photo, `aspect-square`, `rounded-full`; `data-slot="avatar-image"`.
15
+ - `AvatarFallback` — initials on `background-secondary-surface`; `data-slot="avatar-fallback"`.
16
+ - `AvatarBadge` — brand dot pinned bottom-right with a 2px surface ring; scales per size, hides its icon at `sm`; `data-slot="avatar-badge"`.
17
+ - `AvatarGroup` — overlapping stack (`-space-x-2`), each avatar ringed in `background-primary-surface`; `data-slot="avatar-group"`.
18
+ - `AvatarGroupCount` — the "+N" tail circle, sized to match the group's avatars; `data-slot="avatar-group-count"`.
19
+
20
+ ## API
21
+
22
+ ### Avatar
23
+
24
+ | Prop | Type | Default | Notes |
25
+ | --- | --- | --- | --- |
26
+ | `size` | `"sm" \| "default" \| "lg" \| "xl"` | `"default"` | 24 / 32 / 40 / `var(--avatar-size-xl)` px. |
27
+
28
+ All parts also accept Radix/`div` props and `className`.
29
+
30
+ ## Variants & sizes
31
+
32
+ | Size | Diameter | Badge |
33
+ | --- | --- | --- |
34
+ | `sm` | 24px | 8px dot, icon hidden |
35
+ | `default` | 32px | 10px, 8px icon |
36
+ | `lg` | 40px | 12px, 8px icon |
37
+ | `xl` | `--avatar-size-xl` | 14px, 10px icon |
38
+
39
+ ```tsx
40
+ import {
41
+ Avatar, AvatarImage, AvatarFallback, AvatarBadge,
42
+ AvatarGroup, AvatarGroupCount,
43
+ } from "@trading-game/design-intelligence-layer"
44
+
45
+ <Avatar size="lg">
46
+ <AvatarImage src="/u/fahad.png" alt="Fahad" />
47
+ <AvatarFallback>MF</AvatarFallback>
48
+ <AvatarBadge />
49
+ </Avatar>
50
+
51
+ <AvatarGroup>
52
+ <Avatar><AvatarFallback>MF</AvatarFallback></Avatar>
53
+ <Avatar><AvatarFallback>JS</AvatarFallback></Avatar>
54
+ <AvatarGroupCount>+3</AvatarGroupCount>
55
+ </AvatarGroup>
56
+ ```
57
+
58
+ ## Tokens
59
+
60
+ **Colour (semantic):** `background-secondary-surface` + `text-text-subtle-default` (fallback and group count), `background-brand-default` + `text-text-on-brand-static` (badge), `background-primary-surface` (badge ring and group rings).
61
+ **Type (private):**
62
+
63
+ | Token | Value |
64
+ | --- | --- |
65
+ | `--avatar-fallback-*-sm` | 12 / 16 |
66
+ | `--avatar-fallback-*` (default) | 14 / 20 |
67
+ | `--avatar-fallback-*-xl` | 16 / 24 |
68
+ | `--avatar-fallback-font-weight` | regular (400) |
69
+ | `--avatar-group-count-*` | 14 / regular (400) / 20 |
70
+
71
+ ## Behaviour
72
+
73
+ - Radix handles the image-load fallback cascade: `AvatarFallback` shows until `AvatarImage` loads.
74
+ - Size cascades via `data-size` group selectors — the badge and group count read the avatar's size, so `AvatarGroupCount` matches whatever size the group's avatars use (`group-has-data-[size=…]`).
75
+ - `xl` sizing is token-driven (`--avatar-size-xl`), not a Tailwind step.
76
+ - The whole component is `select-none`.
77
+
78
+ ## Do / Don't
79
+
80
+ **Do**
81
+ - Always provide `AvatarFallback` with 1–2 initials.
82
+ - Keep group overlap at the built-in `-space-x-2`; the surface rings depend on it.
83
+ - Put `AvatarGroupCount` last in the group.
84
+
85
+ **Don't**
86
+ - Don't put more than one `AvatarBadge` on an avatar.
87
+ - Don't square the shape — avatars are circles, full stop.
88
+ - Don't bold the initials; fallback weight is regular 400.
@@ -0,0 +1,75 @@
1
+ # Badge
2
+
3
+ Non-interactive status pill — emphasis (solid or tint) crossed with meaning (brand, demo, neutral, success, fail, warning).
4
+
5
+ ## When to use
6
+
7
+ - Labelling state on data: trade result, account mode, market status, counts.
8
+ - Anywhere a compact, read-only classification rides alongside other content.
9
+
10
+ **When not:** For pressable filter/amount pills use [Chip](./chip.md) — Badge never handles selection. For a full-width promotional panel use [Banner](./banner.md).
11
+
12
+ ## Anatomy
13
+
14
+ - `Badge` — a single `span` (or `Slot` with `asChild`); `data-slot="badge"`, `data-variant`. Icons inside are fixed at 12px (`[&>svg]:size-3`).
15
+ - `badgeVariants` — exported cva function for composing the classes elsewhere.
16
+
17
+ ## API
18
+
19
+ ### Badge
20
+
21
+ | Prop | Type | Default | Notes |
22
+ | --- | --- | --- | --- |
23
+ | `variant` | `"solid" \| "solid-demo" \| "solid-neutral" \| "solid-success" \| "solid-fail" \| "solid-warning" \| "tint" \| "tint-demo" \| "tint-neutral" \| "tint-success" \| "tint-fail" \| "tint-warning"` | `"solid"` | Emphasis + meaning grid; brand is the unsuffixed default. |
24
+ | `size` | `"xs" \| "sm" \| "md" \| "lg"` | `"md"` | Heights 20 / 24 / 32 / 40. |
25
+ | `asChild` | `boolean` | `false` | Render onto a caller element. Badges are read-only markers — no hover states exist (interactive pills are Chips). |
26
+
27
+ The `glass` variant was removed (breaking change); there is no replacement — use `tint` on imagery-free surfaces.
28
+
29
+ ## Variants & sizes
30
+
31
+ ```tsx
32
+ import { Badge } from "@trading-game/design-intelligence-layer"
33
+
34
+ <Badge>Live</Badge> {/* solid brand */}
35
+ <Badge variant="solid-success">+2.4%</Badge>
36
+ <Badge variant="solid-fail">-1.1%</Badge>
37
+ <Badge variant="tint">Featured</Badge>
38
+ <Badge variant="tint-warning" size="sm">Delayed</Badge>
39
+ <Badge variant="solid-demo" size="xs">Demo</Badge>
40
+ ```
41
+
42
+ | Size | Height | Padding | Type |
43
+ | --- | --- | --- | --- |
44
+ | `xs` | 20px | `px-2 py-0.5` | 10/16 |
45
+ | `sm` | 24px | `px-2.5 py-0.5` | 12/16 |
46
+ | `md` | 32px | `px-3 py-1` | 14/20 |
47
+ | `lg` | 40px | `px-4 py-1.5` | 16/24 |
48
+
49
+ ## Tokens
50
+
51
+ **Colour (semantic):**
52
+ - Solid: `background-brand-default` + `text-on-brand-static`; `background-demo-solid`; `background-inverse-surface` + `text-prominent-inverse`; `background-success-solid` / `background-error-solid` / `background-warning-solid`, all with `text-on-status-static`.
53
+ - Tint: `--background-brand-container` (layered static recipe, painted via `[background:…]`) + `text-brand-container-static`; `background-demo-default` + `text-demo-default`; `background-secondary-surface` + `border-neutral-default` + `text-prominent-default`; `background-success/error/warning-default` + matching `border-*` and `text-*` status tokens.
54
+ - Focus: `ring-focus-strong`.
55
+
56
+ **Type (private):** `--badge-font-size/line-height-{xs,sm,md,lg}` = 10/16, 12/16, 14/20, 16/24; `--badge-font-weight` = semibold (600).
57
+
58
+ ## Behaviour
59
+
60
+ - Status solids use the bright 600 fills with shared static white ink (`text-on-status-static`) — deliberately below AA on green and amber; a style call, not an oversight.
61
+ - `tint` (brand) paints the layered `--background-brand-container` value via arbitrary `background`, so a `bg-*` utility cannot override it — replace the whole `[background:…]` if you must.
62
+ - No hover states — badges are read-only markers; if it needs to be clickable, it's a [Chip](./chip.md).
63
+ - `rounded-full`, `border-transparent` base (tints add their own status borders), no shadow.
64
+
65
+ ## Do / Don't
66
+
67
+ **Do**
68
+ - Use `solid-success` / `solid-fail` for P&L pills; ink is static white by design.
69
+ - Pick `tint-*` when the badge sits on busy or dense surfaces and needs less weight.
70
+ - Keep icons at the built-in 12px.
71
+
72
+ **Don't**
73
+ - Don't use Badge as a button — it has no press states; that job is [Chip](./chip.md)'s.
74
+ - Don't recreate the removed `glass` variant with alpha overrides.
75
+ - Don't restyle text weight; semibold 600 is fixed and is the system maximum.
@@ -0,0 +1,84 @@
1
+ # Banner
2
+
3
+ Editorial gradient panel — a coloured promotional surface with art, white copy, and a white pill CTA.
4
+
5
+ ## When to use
6
+
7
+ - Promotions, bonuses, feature announcements, and cross-sells that deserve a full-width painted panel.
8
+
9
+ **When not:** For inline status messaging use Section Message; for compact state labels use [Badge](./badge.md).
10
+
11
+ ## Anatomy
12
+
13
+ - Wrapper — a `@container` div so the panel stacks whenever the banner itself (not the viewport) is narrow.
14
+ - `Banner` — the panel; `data-slot="banner"`, `data-clickable` when `onClick` is set; `rounded-2xl`, `p-4` (the card/panel class), horizontal at `@sm`, stacked below.
15
+ - Art slot — 56px frosted square (`--primitive-white-alpha-16`), defaults to a Gift glyph.
16
+ - Copy — title + description, both on static white ink.
17
+ - CTA slot — a string becomes a composed `<Button variant="primary-on-brand" size="sm">` wired to `onAction`; an element takes the slot over.
18
+ - Dismiss — absolute top-right 32px X, rendered only when `onDismiss` is provided.
19
+
20
+ ## API
21
+
22
+ ### Banner
23
+
24
+ | Prop | Type | Default | Notes |
25
+ | --- | --- | --- | --- |
26
+ | `color` | `string` (any CSS colour) | `var(--background-brand-default)` | Solid base; the gradient derives automatically. |
27
+ | `title` | `ReactNode` | — | Required in spirit; renders null (with dev warning) if both title and description are missing. |
28
+ | `description` | `ReactNode` | — | Secondary copy at white 80% alpha. |
29
+ | `art` | `ReactNode \| null` | Gift icon | `null` omits the art square entirely. |
30
+ | `action` | `ReactNode` | — | String → Button primary-on-brand sm (needs `onAction`); element → passes through untouched. |
31
+ | `onAction` | `() => void` | — | Click handler for a string `action` — dev-warns if missing. |
32
+ | `onClick` | `() => void` | — | Whole panel becomes the click target (role="button", Enter/Space). Mutually exclusive with `action`. |
33
+ | `onDismiss` | `() => void` | — | Shows the close X; padding switches to `pr-12`. |
34
+ | `dismissLabel` | `string` | `"Dismiss"` | aria-label for the X. |
35
+
36
+ ## Variants & sizes
37
+
38
+ One variant; the colour is the customisation axis.
39
+
40
+ ```tsx
41
+ import { Banner } from "@trading-game/design-intelligence-layer"
42
+
43
+ <Banner
44
+ title="Weekly tournament"
45
+ description="Top 100 traders share the prize pool."
46
+ action="Join now"
47
+ onDismiss={() => setHidden(true)}
48
+ />
49
+
50
+ <Banner color="#0AA060" title="Jade promo" onClick={openPromo} art={null} />
51
+ ```
52
+
53
+ ## Tokens
54
+
55
+ **Colour (semantic/alpha):** `text-text-on-brand-static` (title, art ink, dismiss), `--primitive-white-alpha-80` (description), `--primitive-white-alpha-16` (frosted art square — an allowed alpha glaze), Button `primary-on-brand` tokens (CTA), `ring-focus-strong` (focus).
56
+ **Type (private):**
57
+
58
+ | Token | Value |
59
+ | --- | --- |
60
+ | `--banner-title-*` | 18 / semibold (600) / 24 |
61
+ | `--banner-description-*` | 12 / medium (500) / 16 |
62
+ | `--banner-action-*` | 12 / semibold (600) / 16 |
63
+
64
+
65
+ ## Behaviour
66
+
67
+ - The fill is a two-layer Figma-style stack: the consumer's solid `color` underneath, a fixed black fade on top (`linear-gradient(120deg, transparent → black 50%)`). Any colour darkens correctly with zero tuning; the default lands on the design file's jade `#0AA060 → #04512E` when given `#0AA060`.
68
+ - The panel is art, not a status surface — it renders identically in light and dark.
69
+ - `onClick` and `action` are mutually exclusive: with both set, the button is suppressed and a dev warning logs; the whole banner is the CTA.
70
+ - With `onDismiss`, `pr-12` reserves the top-right corner so copy never runs under the X; the X stops propagation so it never triggers `onClick`.
71
+ - Clickable banners get hover/active opacity fades and a keyboard-activatable role="button" with the standard focus ring.
72
+ - Layout is container-driven: stacked below `@sm` (CTA goes full-width), horizontal at `@sm+`.
73
+
74
+ ## Do / Don't
75
+
76
+ **Do**
77
+ - Pass game-accent primitives or any hex through `color`; the black fade layer handles contrast.
78
+ - Pass `art={null}` when there is no artwork — do not leave the default Gift on non-gift content.
79
+ - Keep the string CTA with `onAction`; the composed primary-on-brand Button is the standard. The dismiss X is a composed `NavigationButton` (circular per the icon-button law).
80
+
81
+ **Don't**
82
+ - Don't set both `onClick` and `action` — the action is ignored.
83
+ - Don't theme the panel per colour scheme; it is intentionally static in both themes.
84
+ - Don't put a second button inside a clickable banner.
@@ -0,0 +1,90 @@
1
+ # Bottom Navigation
2
+
3
+ Floating mobile navigation pill — a chrome frost bar holding up to four destinations as icon + label stacks, with a liquid selection bubble. The mobile counterpart of the desktop Sidebar, and the carrier of the system's one sanctioned shadow.
4
+
5
+ ## When to use
6
+
7
+ - Top-level mobile navigation between 3–4 primary destinations (3 is the norm: Home, Trade, You).
8
+
9
+ **When not:** On desktop use Sidebar. For hierarchical trails use [Breadcrumb](./breadcrumb.md). For per-page actions use [Button](./button.md).
10
+
11
+ ## Anatomy
12
+
13
+ - `BottomNavigation` — the bar: `h-15` (60px, deliberately off the control rail — floating chrome), `rounded-full`, `px-1`, chrome frost material with the ambient lift; `data-slot="bottom-navigation"`. Rendered as `<nav>`.
14
+ - `BottomNavigationItem` — flex-1 icon + label stack, `rounded-full`, 20px default icon; `data-slot="bottom-navigation-item"`, `data-active`, `aria-current="page"` when active.
15
+ - The liquid bubble — a single `aria-hidden` span (`data-slot="bottom-navigation-bubble"`) that travels to the active item.
16
+ - `BottomNavigationAI` — the optional AI circle (`data-slot="bottom-navigation-ai"`): identical chrome material and lift, circular (`size-15`), Sparkles icon by default (pass children to replace), `active:scale-[0.93]` press squash, default `aria-label="AI assistant"`. Sits beside the bar in a `flex items-center gap-2.5` row; show/hide = render it or not.
17
+
18
+ ## API
19
+
20
+ ### BottomNavigation
21
+
22
+ Standard `nav` props — no variants; the chrome frost is the one material. Dev-only warning when given more than 4 children.
23
+
24
+ ### BottomNavigationItem
25
+
26
+ | Prop | Type | Default | Notes |
27
+ | --- | --- | --- | --- |
28
+ | `active` | `boolean` | `false` | Marks the current destination — the liquid bubble travels to it. |
29
+ | `asChild` | `boolean` | `false` | Render as the child (e.g. a router `<Link>`). |
30
+
31
+ ### BottomNavigationAI
32
+
33
+ | Prop | Type | Default | Notes |
34
+ | --- | --- | --- | --- |
35
+ | `asChild` | `boolean` | `false` | Render as the child (e.g. a router link). |
36
+
37
+ ```tsx
38
+ import { BottomNavigation, BottomNavigationAI, BottomNavigationItem } from "@trading-game/design-intelligence-layer"
39
+ import { Home, LineChart, User } from "lucide-react"
40
+
41
+ <div className="fixed inset-x-4 bottom-4 flex items-center justify-center gap-2.5">
42
+ <BottomNavigation className="max-w-90">
43
+ <BottomNavigationItem active><Home />Home</BottomNavigationItem>
44
+ <BottomNavigationItem><LineChart />Trade</BottomNavigationItem>
45
+ <BottomNavigationItem asChild>
46
+ <a href="/account"><User />You</a>
47
+ </BottomNavigationItem>
48
+ </BottomNavigation>
49
+ <BottomNavigationAI onClick={openAssistant} />
50
+ </div>
51
+ ```
52
+
53
+ ## Tokens
54
+
55
+ **Colour (chrome family — see the Colors page's Chrome group):** `background-chrome-default` (heavy near-solid frost: white 78% light / navy 72% dark, with `blur(20px) saturate(1.4)`), `border-chrome-default`, `shadow-chrome-default` (the one sanctioned shadow — floating chrome over scrolling content), `text-chrome-subtle` (idle ink, resolves through `text-subtle-default`), `text-chrome-selected` (blue-600 light / white dark), `background-chrome-selected` (the bubble's layered top-lit gloss over `background-brand-selected` — paint via `background:`), `chrome-highlight-default` (the bubble's 1px inset specular edge — material rendering, not elevation).
56
+
57
+ **Motion:** travel timing reads the tokens at runtime — `--motion-travel-lead` (200ms leading edge), `--motion-travel-trail` (380ms trailing edge) on `--primitive-ease-spring`; the arriving icon pops on `--motion-pop` × `--primitive-ease-overshoot` (a CSS rule in styles.css — stacked arbitrary variants can't reach descendant svgs).
58
+
59
+ **Type (private):**
60
+
61
+ | Token | Value |
62
+ | --- | --- |
63
+ | `--bottom-navigation-label-*` | 12 / medium (500) / 16 |
64
+
65
+ ## Behaviour
66
+
67
+ - Controlled: the consumer sets `active` on exactly one item; the component holds no state of its own.
68
+ - The component is only the bar — the consumer docks it, typically `fixed inset-x-4 bottom-4`.
69
+ - More than 4 children logs a development warning ("a phone fits at most 4").
70
+ - Items are buttons by default; `asChild` swaps in router links while keeping the styling and `aria-current`.
71
+ - Labels never change size or weight on selection — the bubble and ink swap carry the state.
72
+
73
+ ### The liquid bubble
74
+
75
+ - Selection travels as a single bubble: the leading edge darts ahead on `--motion-travel-lead`, the trailing edge is pulled after on `--motion-travel-trail` — the water-drop absorb. Direction-aware.
76
+ - Geometry: the bubble keeps an equal 4px gap on every side (the bar's `px-1` is the horizontal gap, `inset-y-1` the vertical), so its full-pill radius nests concentrically inside the bar's corners.
77
+ - First paint and resizes re-place the bubble without animating; travel animates only on selection change.
78
+
79
+ ## Do / Don't
80
+
81
+ **Do**
82
+ - Cap at 4 destinations; 3 is the norm and gives the bubble breathing room.
83
+ - Give every item both an icon and a 12px label.
84
+ - Dock with `fixed inset-x-4 bottom-4` so the bar floats above the bottom edge.
85
+ - Render `BottomNavigationAI` beside the bar when the product wants the assistant entry point.
86
+
87
+ **Don't**
88
+ - Don't mark more than one item `active`.
89
+ - Don't add any other shadow — `shadow-chrome-default` is the one exception, already built in.
90
+ - Don't use it on desktop layouts; that is Sidebar territory.
@@ -0,0 +1,85 @@
1
+ # Breadcrumb
2
+
3
+ Hierarchical trail showing where the current page sits, with links back up the tree.
4
+
5
+ ## When to use
6
+
7
+ - Pages three or more levels deep (Markets → Crypto → BTC/USD) where users need one-click ascent.
8
+
9
+ **When not:** For primary mobile navigation use [Bottom Navigation](./bottom-navigation.md). For step-by-step flows use Stepper.
10
+
11
+ ## Anatomy
12
+
13
+ - `Breadcrumb` — `<nav aria-label="breadcrumb">`; `data-slot="breadcrumb"`.
14
+ - `BreadcrumbList` — the `<ol>`, wraps and gaps items, subtle ink; `data-slot="breadcrumb-list"`.
15
+ - `BreadcrumbItem` — one `<li>`; `data-slot="breadcrumb-item"`.
16
+ - `BreadcrumbLink` — ancestor link, hover shifts to prominent ink; `data-slot="breadcrumb-link"`.
17
+ - `BreadcrumbPage` — the current page, brand ink, `aria-current="page"`, `aria-disabled`; `data-slot="breadcrumb-page"`.
18
+ - `BreadcrumbSeparator` — presentation `<li>`, defaults to a 14px ChevronRight; `data-slot="breadcrumb-separator"`.
19
+ - `BreadcrumbEllipsis` — collapsed-levels marker with sr-only "More"; `data-slot="breadcrumb-ellipsis"`.
20
+
21
+ ## API
22
+
23
+ ### BreadcrumbLink
24
+
25
+ | Prop | Type | Default | Notes |
26
+ | --- | --- | --- | --- |
27
+ | `asChild` | `boolean` | `false` | Render a router link instead of `<a>`. |
28
+
29
+ ### BreadcrumbSeparator
30
+
31
+ | Prop | Type | Default | Notes |
32
+ | --- | --- | --- | --- |
33
+ | `children` | `ReactNode` | `<ChevronRight />` | Custom separator glyph. |
34
+
35
+ Other parts take standard element props plus `className`.
36
+
37
+ ## Variants & sizes
38
+
39
+ One variant, one size.
40
+
41
+ ```tsx
42
+ import {
43
+ Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink,
44
+ BreadcrumbSeparator, BreadcrumbPage, BreadcrumbEllipsis,
45
+ } from "@trading-game/design-intelligence-layer"
46
+
47
+ <Breadcrumb>
48
+ <BreadcrumbList>
49
+ <BreadcrumbItem><BreadcrumbLink href="/markets">Markets</BreadcrumbLink></BreadcrumbItem>
50
+ <BreadcrumbSeparator />
51
+ <BreadcrumbItem><BreadcrumbEllipsis /></BreadcrumbItem>
52
+ <BreadcrumbSeparator />
53
+ <BreadcrumbItem><BreadcrumbPage>BTC/USD</BreadcrumbPage></BreadcrumbItem>
54
+ </BreadcrumbList>
55
+ </Breadcrumb>
56
+ ```
57
+
58
+ ## Tokens
59
+
60
+ **Colour (semantic):** `text-text-subtle-default` (trail), `text-text-prominent-default` (link hover), `text-text-brand-default` (current page).
61
+ **Type (private):**
62
+
63
+ | Token | Value |
64
+ | --- | --- |
65
+ | `--breadcrumb-*` | 14 / regular (400) / 20 |
66
+ | `--breadcrumb-page-font-weight` | medium (500) |
67
+
68
+ ## Behaviour
69
+
70
+ - Entirely static markup — no state; the consumer builds the trail.
71
+ - `BreadcrumbPage` is a `span` with `role="link" aria-disabled="true" aria-current="page"` — it is not clickable.
72
+ - Separators are `role="presentation" aria-hidden` and never announced.
73
+ - The list wraps (`flex-wrap`) with gap 6px, 10px at `sm`.
74
+
75
+ ## Do / Don't
76
+
77
+ **Do**
78
+ - End every trail with `BreadcrumbPage`, never a link.
79
+ - Collapse deep trails with `BreadcrumbEllipsis` rather than shrinking type below 14.
80
+ - Put a `BreadcrumbSeparator` between items, outside the `BreadcrumbItem`.
81
+
82
+ **Don't**
83
+ - Don't style the current page heavier than medium 500.
84
+ - Don't make the current page clickable.
85
+ - Don't use breadcrumbs as tabs or primary navigation.