@trading-game/design-intelligence-layer 0.17.4 → 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 -746
  3. package/dist/index.cjs +2753 -2986
  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 -2856
  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 +5 -6
  76. package/src/styles.css +1616 -251
  77. package/guides/design-system-guide/trading-game-ds-guide.md +0 -933
@@ -0,0 +1,66 @@
1
+ # Scroll Area
2
+
3
+ Custom-styled scroll container (Radix ScrollArea) with a themed thumb replacing the native scrollbar.
4
+
5
+ ## When to use
6
+
7
+ - Fixed-height regions inside cards, menus, and panels where the native scrollbar would clash with the surface.
8
+ - Horizontal strips (chips, thumbnails) via a `ScrollBar orientation="horizontal"`.
9
+
10
+ **When not:** For whole-page scrolling use the document scrollbar. For resizable split panes use [Resizable](./resizable.md).
11
+
12
+ ## Anatomy
13
+
14
+ - `ScrollArea` — Radix root (`relative`), renders Viewport + vertical `ScrollBar` + Corner automatically; `data-slot="scroll-area"`.
15
+ - Viewport — internal, `size-full rounded-[inherit]`, focusable with the standard control ring; `data-slot="scroll-area-viewport"`.
16
+ - `ScrollBar` — 10px-wide (`w-2.5` / `h-2.5`) scrollbar rail; `data-slot="scroll-area-scrollbar"`.
17
+ - Thumb — internal `rounded-full` pill; `data-slot="scroll-area-thumb"`.
18
+
19
+ ## API
20
+
21
+ ### ScrollArea
22
+
23
+ | Prop | Type | Default | Notes |
24
+ | --- | --- | --- | --- |
25
+ | `children` | `ReactNode` | — | Rendered inside the viewport. |
26
+ | ...props | Radix `ScrollArea.Root` props | — | e.g. `type`, `scrollHideDelay`. |
27
+
28
+ ### ScrollBar
29
+
30
+ | Prop | Type | Default | Notes |
31
+ | --- | --- | --- | --- |
32
+ | `orientation` | `"vertical" \| "horizontal"` | `"vertical"` | A vertical bar is included by default; add a horizontal one yourself. |
33
+
34
+ ## Variants & sizes
35
+
36
+ No variants; height comes from your own classes.
37
+
38
+ ```tsx
39
+ import { ScrollArea, ScrollBar } from "@trading-game/design-intelligence-layer"
40
+
41
+ <ScrollArea className="h-72 rounded-lg border border-border-default-default">
42
+ {/* long content */}
43
+ <ScrollBar orientation="horizontal" />
44
+ </ScrollArea>
45
+ ```
46
+
47
+ ## Tokens
48
+
49
+ **Colour (semantic):** `bg-border-default-default` (thumb), `ring-ring-focus-strong` (viewport keyboard focus, `focus-visible:ring-[3px]`).
50
+ **Type (private):** none — the component renders no text.
51
+
52
+ ## Behaviour
53
+
54
+ - `ScrollArea` always mounts a vertical `ScrollBar`; horizontal scrolling needs an explicit second `<ScrollBar orientation="horizontal" />` child.
55
+ - The viewport inherits the root's radius (`rounded-[inherit]`), so putting the radius on `ScrollArea` clips content correctly.
56
+ - The viewport is keyboard-focusable and takes the standard `focus-visible:ring-[3px] ring-ring-focus-strong` control ring.
57
+
58
+ ## Do / Don't
59
+
60
+ **Do**
61
+ - Give `ScrollArea` an explicit height (or max-height); without one nothing scrolls.
62
+ - Put border and radius on the `ScrollArea` root so the viewport inherits the clip.
63
+
64
+ **Don't**
65
+ - Don't restyle the thumb with brand colour — it is a neutral `border-default-default` pill by design.
66
+ - Don't nest a `ScrollArea` inside another scroll container on the same axis; wheel events become ambiguous.
@@ -0,0 +1,99 @@
1
+ # Section Message
2
+
3
+ Inline status band (renamed from Alert) that reports info, warning, danger, or success context for a section of the page.
4
+
5
+ ## When to use
6
+
7
+ - Persistent, contextual status tied to a section or form — verification pending, deposit failed, settings saved.
8
+ - Messages that may carry one or two actions and an optional dismiss.
9
+
10
+ **When not:** For transient confirmations use [Toast](./toast.md). For full-width promotional or app-level strips use Banner. For blocking confirmations use Alert Dialog.
11
+
12
+ ## Anatomy
13
+
14
+ Single component, slot-driven; `data-slot="section-message"` with `data-variant`, `data-layout`, `data-size` attributes.
15
+
16
+ - Icon column — per-variant default icon (Info / AlertTriangle / XCircle / CheckCircle2), the only element that carries severity colour.
17
+ - Body — title (semibold) + description (regular), both `text-text-prominent-default`.
18
+ - Action slot — one button beside the text (`row`) or up to two below it (`stack`).
19
+ - Close X — renders only when `onDismiss` is provided; neutral `icon-subtle` at 70% opacity.
20
+
21
+ ## API
22
+
23
+ | Prop | Type | Default | Notes |
24
+ | --- | --- | --- | --- |
25
+ | `variant` | `"info" \| "warning" \| "danger" \| "success"` | `"info"` | Surface + border + icon colour. |
26
+ | `layout` | `"row" \| "stack" \| "inline" \| "card"` | `"row"` | `inline` and `card` are aliases — `inline`→`row`, `card`→`stack`. @deprecated — removed at v1.0. |
27
+ | `size` | `"sm" \| "md"` | `"md"` | Strict one-step ladder: padding 16→12, gaps 12→8, icon 16→14, radius xl→lg, type 14/20→12/16. |
28
+ | `title` | `ReactNode` | — | At least one of `title`/`description` is required; otherwise renders `null` with a dev warning. |
29
+ | `description` | `ReactNode` | — | Regular weight; hierarchy vs. title is weight-only (600 vs 400). |
30
+ | `icon` | `ReactNode \| null` | variant default | `null` omits the icon; any node overrides it. |
31
+ | `action` | `ReactNode` | — | Honoured only in `layout="row"`; primitives get wrapped in `<Button variant="secondary" size="sm">`. |
32
+ | `actions` | `ReactNode[]` | — | Honoured only in `layout="stack"`; capped at 2. |
33
+ | `onDismiss` | `() => void` | — | Presence renders the close X. |
34
+ | `dismissLabel` | `string` | `"Dismiss"` | aria-label for the close button. |
35
+
36
+ Deprecated aliases: `Alert`, `alertVariants`, `AlertProps`, `AlertVariant`, `AlertLayout` — @deprecated — removed at v1.0.
37
+
38
+ ## Variants & sizes
39
+
40
+ ```tsx
41
+ import { SectionMessage, Button } from "@trading-game/design-intelligence-layer"
42
+
43
+ <SectionMessage variant="info" title="Verification pending" description="Usually takes under 2 minutes." />
44
+
45
+ <SectionMessage
46
+ variant="danger"
47
+ layout="row"
48
+ title="Deposit failed"
49
+ action={<Button variant="secondary" size="sm">Retry</Button>}
50
+ onDismiss={() => {}}
51
+ />
52
+
53
+ <SectionMessage
54
+ variant="warning"
55
+ layout="stack"
56
+ size="sm"
57
+ title="Margin call risk"
58
+ description="Your equity is below 50% of used margin."
59
+ actions={["Add funds", "Close positions"]}
60
+ />
61
+ ```
62
+
63
+ ## Tokens
64
+
65
+ **Colour (semantic):**
66
+ - Surfaces/borders: `bg-background-information-default` + `border-border-information-default` (info), same pattern with `warning`, `error` (danger variant), `success`.
67
+ - Title/description ink: `text-text-prominent-default` on every variant.
68
+ - Icon ink: `text-icon-information-default` / `-warning-` / `-error-` / `-success-` — the only severity colour signal.
69
+ - Close X: a composed `NavigationButton` (circular per the icon-button law) carrying the tone ink at 70% opacity; focus `ring-ring-focus-strong` at 3px.
70
+
71
+ **Type (private):**
72
+
73
+ | Token | Value |
74
+ | --- | --- |
75
+ | `--section-message-title-*` (md) | 14 / semibold (600) / 20 |
76
+ | `--section-message-description-*` (md) | 14 / regular (400) / 20 |
77
+ | `--section-message-title-*-sm` | 12 / semibold (600) / 16 |
78
+ | `--section-message-description-*-sm` | 12 / regular (400) / 16 |
79
+
80
+ ## Behaviour
81
+
82
+ - `danger` and `warning` render `role="alert"` + `aria-live="assertive"`; `info` and `success` use `role="status"` + `polite`.
83
+ - Row layout is mobile-first: 3 columns × 2 rows so the action drops below the body; at `sm:` (640px) it collapses to one row of 4 columns (icon · body · action · close).
84
+ - Single-line row messages (title only or description only) centre vertically and hold a resting min-height of 48px (md) / 40px (sm), growing when text wraps.
85
+ - Actions are normalized: React elements pass through untouched; string/number actions get wrapped in a Secondary `sm` Button. Dev-mode warns when `actions` is passed to `row` or `action` to `stack`.
86
+ - Rendering with neither `title` nor `description` returns `null` (dev warning).
87
+
88
+ ## Do / Don't
89
+
90
+ **Do**
91
+ - Let the icon alone carry severity — title and description stay `text-prominent` on all four variants.
92
+ - Use Secondary `sm` buttons for actions; promotional surfaces that need primary-inverse buttons belong on Banner.
93
+ - Pass `onDismiss` only when dismissing is genuinely meaningful; its absence removes the X entirely.
94
+
95
+ **Don't**
96
+ - Don't use the deprecated `inline`/`card` layout values or the `Alert` alias in new code — both go at v1.0.
97
+ - Don't exceed two actions in `stack`; the component slices at 2.
98
+ - Don't bold the description or push the title past semibold 600 — the 600/400 weight pair is the entire hierarchy.
99
+ - Don't add shadows — status bands are flat tinted surfaces with a hairline border.
@@ -0,0 +1,105 @@
1
+ # Select
2
+
3
+ Single-choice dropdown (Radix Select) with a form-input trigger and a menu-styled option list.
4
+
5
+ ## When to use
6
+
7
+ - Choosing one value from 5–15 options inside a form — account currency, leverage, market.
8
+ - When the current value must read like an input, sitting on the 32/40/48 field rail.
9
+
10
+ **When not:** For 2–4 always-visible options use [Toggle Group](./toggle-group.md) or Tabs. For command-style search over long lists use Command. For native mobile pickers use Native Select.
11
+
12
+ ## Anatomy
13
+
14
+ - `Select` / `SelectGroup` / `SelectValue` — Radix pass-throughs; `data-slot="select"`, `"select-group"`, `"select-value"`.
15
+ - `SelectTrigger` — input-shaped button, `rounded-xs` hairline on `bg-background-primary-surface`, chevron at 50% opacity; `data-slot="select-trigger"`, `data-size`, `data-readonly`.
16
+ - `SelectContent` — portalled `rounded-lg` list on primary surface; `data-slot="select-content"`.
17
+ - `SelectItem` — `rounded-md` row with a right-aligned brand check when selected; `data-slot="select-item"` (+ `select-item-indicator`).
18
+ - `SelectLabel` — group heading, `type-menu-heading`, subtle ink; `data-slot="select-label"`.
19
+ - `SelectSeparator` — 1px hairline between groups; `data-slot="select-separator"`.
20
+ - `SelectScrollUpButton` / `SelectScrollDownButton` — chevron rows for overflowing lists.
21
+
22
+ ## API
23
+
24
+ ### SelectTrigger
25
+
26
+ | Prop | Type | Default | Notes |
27
+ | --- | --- | --- | --- |
28
+ | `size` | `"sm" \| "md" \| "lg"` | `"md"` | sm `h-8 px-3`, md `h-10 px-3`, lg `h-12 px-4` — the 32/40/48 rail. |
29
+ | `readOnly` | `boolean` | `false` | Locked but reachable: stays focusable, announces `aria-readonly`, never opens (open triggers are swallowed); full opacity, cursor default, chevron at 30%. |
30
+ | ...props | Radix `Trigger` props | — | `disabled` etc. |
31
+
32
+ ### SelectContent
33
+
34
+ | Prop | Type | Default | Notes |
35
+ | --- | --- | --- | --- |
36
+ | `position` | `"popper" \| "item-aligned"` | `"popper"` | Deliberate override of the Radix default — see Behaviour. |
37
+ | `align` | `"start" \| "center" \| "end"` | `"start"` | Popper alignment. |
38
+
39
+ ### Select (root)
40
+
41
+ | Prop | Type | Default | Notes |
42
+ | --- | --- | --- | --- |
43
+ | `value` / `defaultValue` | `string` | — | Controlled / uncontrolled. |
44
+ | `onValueChange` | `(value: string) => void` | — | Radix callback. |
45
+
46
+ `SelectItem` requires `value: string`; other parts take standard Radix props plus `className`.
47
+
48
+ ## Variants & sizes
49
+
50
+ Trigger sizes only — the type ladder is picked in JSX (`type-select-sm/md/lg`) because `.type-*` classes are plain CSS and Tailwind `data-[]` variants cannot gate them.
51
+
52
+ ```tsx
53
+ import {
54
+ Select, SelectTrigger, SelectValue, SelectContent,
55
+ SelectGroup, SelectLabel, SelectItem, SelectSeparator,
56
+ } from "@trading-game/design-intelligence-layer"
57
+
58
+ <Select defaultValue="usdt">
59
+ <SelectTrigger size="md" className="w-48">
60
+ <SelectValue placeholder="Currency" />
61
+ </SelectTrigger>
62
+ <SelectContent>
63
+ <SelectGroup>
64
+ <SelectLabel>Stablecoins</SelectLabel>
65
+ <SelectItem value="usdt">USDT</SelectItem>
66
+ <SelectItem value="usdc">USDC</SelectItem>
67
+ </SelectGroup>
68
+ <SelectSeparator />
69
+ <SelectItem value="btc">BTC</SelectItem>
70
+ </SelectContent>
71
+ </Select>
72
+ ```
73
+
74
+ ## Tokens
75
+
76
+ **Colour (semantic):** `border-border-default-default` + `bg-background-primary-surface` + `text-text-prominent-default` (trigger and content), `text-text-subtle-default` (placeholder, labels), `icon-subtle-default` (chevron, item icons), `bg-background-hover-default` (item focus row), `text-text-brand-selected` (checked item text and check icon), `border-border-error-default` + `ring-error-soft` (invalid), soft focus `ring-ring-focus-soft` at 3px.
77
+ **Type (private):**
78
+
79
+ | Token | Value |
80
+ | --- | --- |
81
+ | `--select-*-sm` (trigger) | 12 / regular (400) / 16 |
82
+ | `--select-*-md` (trigger) | 14 / regular (400) / 20 |
83
+ | `--select-*-lg` (trigger) | 16 / regular (400) / 24 |
84
+ | Rows / labels | shared `--menu-*` family — item 14/regular/20, heading 12/medium/16 |
85
+
86
+ ## Behaviour
87
+
88
+ - `SelectContent` defaults to `position="popper"`, opening the list **below** the trigger. The Radix default (`item-aligned`) was deliberately replaced because it slides the list to overlap the trigger, which reads as a bug next to the other menus.
89
+ - In popper mode the viewport is sized to the trigger (`min-w-[var(--radix-select-trigger-width)]`), and the list is offset 1 unit from the trigger edge.
90
+ - Checked items show `text-text-brand-selected` ink plus a brand `CheckIcon` in the right indicator slot — no fill change on the row.
91
+ - `readOnly` keeps the trigger in the tab order and announced (unlike `disabled`) — keyboard and screen-reader users can still reach and hear the locked value; it just never opens.
92
+ - The trigger uses the text-field soft focus ring (`ring-focus-soft`), not the 50% control ring.
93
+
94
+ ## Do / Don't
95
+
96
+ **Do**
97
+ - Keep triggers on the 32/40/48 rail via `size`; set width with `className`, not padding overrides.
98
+ - Use `SelectLabel` + `SelectSeparator` to group long lists; labels are 12/medium/16 subtle ink.
99
+ - Use `readOnly` (not `disabled`) for locked-but-meaningful values.
100
+
101
+ **Don't**
102
+ - Don't pass `position="item-aligned"` — the popper-below behaviour is a system decision.
103
+ - Don't restyle the checked state with a background fill; selection here is ink + check icon only.
104
+ - Don't gate the trigger type with `data-[size]` utilities — `.type-select-*` is plain CSS and beats `font-*` utilities; the size prop already picks it in JSX.
105
+ - Don't add shadows to `SelectContent` — menus are solid surfaces with a hairline border.
@@ -0,0 +1,55 @@
1
+ # Separator
2
+
3
+ A 1px hairline divider (Radix Separator), horizontal or vertical.
4
+
5
+ ## When to use
6
+
7
+ - Dividing groups within a surface — menu sections, toolbar clusters, list segments — where spacing alone is not enough.
8
+
9
+ **When not:** For a draggable divider between panes use [Resizable](./resizable.md). Inside tables, rows already carry their own hairline borders.
10
+
11
+ ## Anatomy
12
+
13
+ - `Separator` — single element; `h-px w-full` horizontal, `w-px h-full` vertical via `data-[orientation]`; `data-slot="separator"`.
14
+
15
+ ## API
16
+
17
+ | Prop | Type | Default | Notes |
18
+ | --- | --- | --- | --- |
19
+ | `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Drives the `data-orientation` styling. |
20
+ | `decorative` | `boolean` | `true` | `true` removes it from the a11y tree; set `false` only for semantic separation. |
21
+
22
+ ## Variants & sizes
23
+
24
+ One variant — always a 1px `border-default-default` line.
25
+
26
+ ```tsx
27
+ import { Separator } from "@trading-game/design-intelligence-layer"
28
+
29
+ <Separator className="my-4" />
30
+ <div className="flex h-5 items-center gap-4">
31
+ <span>Spot</span>
32
+ <Separator orientation="vertical" />
33
+ <span>Futures</span>
34
+ </div>
35
+ ```
36
+
37
+ ## Tokens
38
+
39
+ **Colour (semantic):** `bg-border-default-default`.
40
+ **Type (private):** none.
41
+
42
+ ## Behaviour
43
+
44
+ - Vertical orientation needs a parent with a resolved height (`h-full` on the separator fills it).
45
+ - `decorative` defaults to `true`, so screen readers skip it by default.
46
+
47
+ ## Do / Don't
48
+
49
+ **Do**
50
+ - Keep it at 1px and `border-default-default` — every divider in the system is the same hairline.
51
+ - Control spacing with margin utilities (`my-4`, `mx-2`) on the separator, not wrapper divs.
52
+
53
+ **Don't**
54
+ - Don't recolour or thicken it to create emphasis; use spacing or type hierarchy instead.
55
+ - Don't stack a Separator against an element that already has a border — doubles the line.
@@ -0,0 +1,91 @@
1
+ # Sheet
2
+
3
+ Edge-anchored overlay panel (built on Radix Dialog) that slides in from a side of the viewport.
4
+
5
+ ## When to use
6
+
7
+ - Secondary flows that keep page context — filters, order detail, settings panels.
8
+ - The desktop/tablet counterpart to Drawer; the mobile [Sidebar](./sidebar.md) also renders inside a Sheet.
9
+
10
+ **When not:** For centered blocking confirmation use Dialog / Alert Dialog. For mobile bottom trays use Drawer. For persistent navigation use [Sidebar](./sidebar.md).
11
+
12
+ ## Anatomy
13
+
14
+ - `Sheet` / `SheetTrigger` / `SheetClose` / `SheetPortal` — Radix Dialog pass-throughs; `data-slot="sheet"`, `"sheet-trigger"`, `"sheet-close"`, `"sheet-portal"`.
15
+ - `SheetOverlay` — full-viewport scrim, `bg-background-overlay-default`; `data-slot="sheet-overlay"`.
16
+ - `SheetContent` — the sliding panel: solid `bg-background-primary-surface`, one hairline border on the attached edge, no radius, no shadow; built-in circular close button; `data-slot="sheet-content"`.
17
+ - `SheetHeader` / `SheetFooter` — `p-4` column layout; footer is `mt-auto`; `data-slot="sheet-header"` / `"sheet-footer"`.
18
+ - `SheetTitle` — `type-sheet-title` prominent ink; `data-slot="sheet-title"`.
19
+ - `SheetDescription` — `type-sheet-description` subtle ink; `data-slot="sheet-description"`.
20
+
21
+ ## API
22
+
23
+ ### Sheet (root)
24
+
25
+ | Prop | Type | Default | Notes |
26
+ | --- | --- | --- | --- |
27
+ | `open` / `defaultOpen` | `boolean` | — | Controlled / uncontrolled. |
28
+ | `onOpenChange` | `(open: boolean) => void` | — | Radix callback. |
29
+
30
+ ### SheetContent
31
+
32
+ | Prop | Type | Default | Notes |
33
+ | --- | --- | --- | --- |
34
+ | `side` | `"top" \| "right" \| "bottom" \| "left"` | `"right"` | Anchor edge; left/right are `w-3/4 sm:max-w-sm`, top/bottom are `h-auto`. |
35
+ | `showCloseButton` | `boolean` | `true` | Toggles the built-in circular close (size-8, top-right). |
36
+
37
+ Other parts take standard Radix/div props plus `className`.
38
+
39
+ ## Variants & sizes
40
+
41
+ No size prop — the `side` value is the only axis.
42
+
43
+ ```tsx
44
+ import {
45
+ Sheet, SheetTrigger, SheetContent, SheetHeader,
46
+ SheetTitle, SheetDescription, SheetFooter, SheetClose,
47
+ Button,
48
+ } from "@trading-game/design-intelligence-layer"
49
+
50
+ <Sheet>
51
+ <SheetTrigger asChild><Button variant="secondary">Filters</Button></SheetTrigger>
52
+ <SheetContent side="right">
53
+ <SheetHeader>
54
+ <SheetTitle>Filters</SheetTitle>
55
+ <SheetDescription>Narrow the market list.</SheetDescription>
56
+ </SheetHeader>
57
+ <SheetFooter>
58
+ <SheetClose asChild><Button variant="primary">Apply</Button></SheetClose>
59
+ </SheetFooter>
60
+ </SheetContent>
61
+ </Sheet>
62
+ ```
63
+
64
+ ## Tokens
65
+
66
+ **Colour (semantic):** `bg-background-overlay-default` (scrim), `bg-background-primary-surface` (panel), `border-border-default-default` (hairline on the attached edge: `border-l` for right, `border-r` for left, `border-b` for top, `border-t` for bottom), `text-text-prominent-default` (title), `text-text-subtle-default` (description), `text-icon-subtle-default` + `bg-background-hover-default` hover glaze (close button), `ring-ring-focus-strong` at 3px (close focus).
67
+ **Type (private):**
68
+
69
+ | Token | Value |
70
+ | --- | --- |
71
+ | `--sheet-title-*` | 18 / semibold (600) / 24 |
72
+ | `--sheet-description-*` | 14 / regular (400) / 20 |
73
+
74
+ ## Behaviour
75
+
76
+ - Enter/exit are directional slides (`slide-in-from-*` / `slide-out-to-*`) on the overlay motion role: `--motion-overlay-open` (320ms, `ease-enter`) in and `--motion-overlay-close` (250ms, `ease-exit`) out; the overlay fades.
77
+ - Left/right sheets are 75% viewport width capped at `sm:max-w-sm`; top/bottom size to content.
78
+ - The built-in close is a `size-8` circle (`rounded-full`) at 70% opacity that gains the `background-hover-default` glaze on hover — the icon-buttons-are-circles rule.
79
+ - The panel has a hairline only on its attached edge and no rounded corners on any side — it meets the viewport edges flush.
80
+
81
+ ## Do / Don't
82
+
83
+ **Do**
84
+ - Always include `SheetTitle` (visually or `sr-only`) — Radix Dialog requires it for a11y; the mobile Sidebar does exactly this.
85
+ - Put primary actions in `SheetFooter`; it pins to the bottom via `mt-auto`.
86
+ - Keep the built-in close unless the sheet supplies its own explicit dismissal.
87
+
88
+ **Don't**
89
+ - Don't add shadows or rounded edges to `SheetContent` — the edge hairline is the entire separation; cards/dialogs get `rounded-2xl`, sheets get none.
90
+ - Don't push the title past semibold 600.
91
+ - Don't stack sheets; open at most one overlay layer at a time.
@@ -0,0 +1,125 @@
1
+ # Sidebar
2
+
3
+ Desktop navigation rail with collapsible states, grouped menus, and an automatic mobile Sheet fallback.
4
+
5
+ ## When to use
6
+
7
+ - Primary app navigation on desktop — market sections, account areas, tool groups.
8
+ - Layouts that need an icon-collapsed rail (`collapsible="icon"`) to reclaim width.
9
+
10
+ **When not:** For mobile primary navigation use Bottom Navigation. For transient side panels use [Sheet](./sheet.md) directly.
11
+
12
+ ## Anatomy
13
+
14
+ - `SidebarProvider` — context + state owner; sets `--sidebar-width` (16rem) / `--sidebar-width-icon` (3rem); `data-slot="sidebar-wrapper"`.
15
+ - `Sidebar` — the rail; on mobile renders inside a `Sheet` (18rem wide, hidden built-in close); `data-slot="sidebar"` with `data-state`, `data-collapsible`, `data-variant`, `data-side`.
16
+ - `SidebarTrigger` — Secondary icon Button (`size-7`) that toggles; `data-slot="sidebar-trigger"`.
17
+ - `SidebarRail` — invisible edge strip for click/drag-style toggling; `data-slot="sidebar-rail"`.
18
+ - `SidebarInset` — the `main` content area (`bg-background-primary-canvas`); insets itself when `variant="inset"`; `data-slot="sidebar-inset"`.
19
+ - `SidebarHeader` / `SidebarContent` / `SidebarFooter` — vertical regions; `data-slot="sidebar-header"` / `"sidebar-content"` / `"sidebar-footer"`.
20
+ - `SidebarGroup` / `SidebarGroupLabel` / `SidebarGroupAction` / `SidebarGroupContent` — labelled sections; label is `type-sidebar-label` subtle ink and slides away when icon-collapsed.
21
+ - `SidebarMenu` / `SidebarMenuItem` / `SidebarMenuButton` / `SidebarMenuAction` / `SidebarMenuBadge` / `SidebarMenuSkeleton` — the nav rows.
22
+ - `SidebarMenuSub` / `SidebarMenuSubItem` / `SidebarMenuSubButton` — nested rows behind a hairline left border; hidden when icon-collapsed.
23
+ - `SidebarInput` / `SidebarSeparator` — Input and Separator tuned for the rail.
24
+ - `useSidebar()` — hook exposing `state`, `open`, `setOpen`, `openMobile`, `setOpenMobile`, `isMobile`, `toggleSidebar`; throws outside `SidebarProvider`.
25
+
26
+ ## API
27
+
28
+ ### SidebarProvider
29
+
30
+ | Prop | Type | Default | Notes |
31
+ | --- | --- | --- | --- |
32
+ | `defaultOpen` | `boolean` | `true` | Uncontrolled initial state. |
33
+ | `open` / `onOpenChange` | `boolean` / `(open) => void` | — | Controlled mode. |
34
+
35
+ ### Sidebar
36
+
37
+ | Prop | Type | Default | Notes |
38
+ | --- | --- | --- | --- |
39
+ | `side` | `"left" \| "right"` | `"left"` | Anchor edge. |
40
+ | `variant` | `"sidebar" \| "floating" \| "inset"` | `"sidebar"` | `floating` gets `rounded-lg` + hairline; `inset` pads the main area. |
41
+ | `collapsible` | `"offcanvas" \| "icon" \| "none"` | `"offcanvas"` | `offcanvas` slides fully away; `icon` collapses to the 3rem rail; `none` is static. |
42
+
43
+ ### SidebarMenuButton
44
+
45
+ | Prop | Type | Default | Notes |
46
+ | --- | --- | --- | --- |
47
+ | `isActive` | `boolean` | `false` | Sets `data-active` — brand-selected fill + ink + semibold. |
48
+ | `variant` | `"default" \| "outline"` | `"default"` | Outline adds `ring-1 ring-border-default-default` on primary surface. |
49
+ | `size` | `"default" \| "sm" \| "lg"` | `"default"` | h-8 / h-7 / h-12; sm uses `type-sidebar-menu-sm`. |
50
+ | `tooltip` | `string \| TooltipContent props` | — | Shown only while collapsed on desktop. |
51
+ | `asChild` | `boolean` | `false` | Slot passthrough for links. |
52
+
53
+ ### SidebarMenuSubButton
54
+
55
+ | Prop | Type | Default | Notes |
56
+ | --- | --- | --- | --- |
57
+ | `size` | `"sm" \| "md"` | `"md"` | 12/16 vs 14/20 type. |
58
+ | `isActive` | `boolean` | `false` | Same brand-selected treatment as top rows. |
59
+ | `asChild` | `boolean` | `false` | Renders an `<a>` by default. |
60
+
61
+ `SidebarMenuAction` adds `showOnHover?: boolean`; `SidebarMenuSkeleton` adds `showIcon?: boolean`.
62
+
63
+ ## Variants & sizes
64
+
65
+ ```tsx
66
+ import {
67
+ SidebarProvider, Sidebar, SidebarContent, SidebarGroup,
68
+ SidebarGroupLabel, SidebarMenu, SidebarMenuItem,
69
+ SidebarMenuButton, SidebarTrigger, SidebarInset,
70
+ } from "@trading-game/design-intelligence-layer"
71
+
72
+ <SidebarProvider>
73
+ <Sidebar collapsible="icon">
74
+ <SidebarContent>
75
+ <SidebarGroup>
76
+ <SidebarGroupLabel>Markets</SidebarGroupLabel>
77
+ <SidebarMenu>
78
+ <SidebarMenuItem>
79
+ <SidebarMenuButton isActive tooltip="Forex">Forex</SidebarMenuButton>
80
+ </SidebarMenuItem>
81
+ </SidebarMenu>
82
+ </SidebarGroup>
83
+ </SidebarContent>
84
+ </Sidebar>
85
+ <SidebarInset>
86
+ <SidebarTrigger />
87
+ {/* page */}
88
+ </SidebarInset>
89
+ </SidebarProvider>
90
+ ```
91
+
92
+ ## Tokens
93
+
94
+ **Colour (semantic):** `bg-background-primary-surface` (rail), `text-text-prominent-default` (rows), `bg-background-hover-default` + `text-text-brand-hover` (row hover/active-press), `bg-background-brand-selected` + `text-text-brand-selected` (active rows, icons included), `text-text-subtle-default` (group labels), `border-border-default-default` (rail edge, sub-menu left hairline, separators), `bg-background-secondary-surface` (action-button hover), `ring-ring-focus-default` (focus, `ring-2`).
95
+ **Type (private):**
96
+
97
+ | Token | Value |
98
+ | --- | --- |
99
+ | `--sidebar-menu-*` | 14 / regular (400) / 20 |
100
+ | `--sidebar-menu-*-sm` | 12 / regular (400) / 16 |
101
+ | `--sidebar-menu-active-font-weight` | semibold (600), applied via CSS `[data-sidebar="menu-button"][data-active="true"]` |
102
+ | `--sidebar-label-*` (labels and badges) | 12 / medium (500) / 16 |
103
+
104
+ Legacy `--sidebar` / `--sidebar-foreground` / `--sidebar-accent` aliases still exist in `styles.css` mapped onto semantic tokens.
105
+
106
+ ## Behaviour
107
+
108
+ - State lives in `SidebarProvider`; desktop open state persists in the `sidebar_state` cookie for 7 days. Controlled mode via `open`/`onOpenChange`.
109
+ - Keyboard shortcut: cmd/ctrl+B toggles the sidebar globally.
110
+ - On mobile (`useIsMobile`) the rail renders inside a `SheetContent` at 18rem with the Sheet's own close hidden and an `sr-only` title/description for a11y; `openMobile`/`setOpenMobile` drive it.
111
+ - Active rows go semibold through the CSS data-attribute rule, not a `font-*` utility — `.type-sidebar-menu` is plain CSS and Tailwind variants can't gate it.
112
+ - When collapsed to `icon`, group labels animate away (`-mt-8 opacity-0`), sub-menus/badges/actions hide, and `SidebarMenuButton` tooltips activate (side="right").
113
+ - Width transitions run at constant speed (`ease-linear`, `duration-base`).
114
+
115
+ ## Do / Don't
116
+
117
+ **Do**
118
+ - Mark exactly one row per view `isActive` — brand-selected fill (light: 10% brand tint; dark: solid `#121A55`-family fill with blue-200 ink) is the selection signal.
119
+ - Provide `tooltip` on every `SidebarMenuButton` when using `collapsible="icon"`; labels vanish in the rail state.
120
+ - Wrap the whole layout — rail and `SidebarInset` — in one `SidebarProvider`.
121
+
122
+ **Don't**
123
+ - Don't apply `font-semibold` to active rows yourself — the `data-active` CSS rule already does it, and `.type-sidebar-menu` beats `font-*` utilities anyway.
124
+ - Don't push any row weight past semibold 600 or add shadows to the rail — it is a flat primary surface with a hairline edge.
125
+ - Don't call `useSidebar()` outside `SidebarProvider`; it throws.
@@ -0,0 +1,51 @@
1
+ # Skeleton
2
+
3
+ Pulsing placeholder block shown while real content loads.
4
+
5
+ ## When to use
6
+
7
+ - Loading states for content with a known shape — table rows, cards, avatars, nav items (see `SidebarMenuSkeleton`).
8
+
9
+ **When not:** For indeterminate action feedback (button press, background task) use [Spinner](./spinner.md). For measured progress use Progress.
10
+
11
+ ## Anatomy
12
+
13
+ - `Skeleton` — a single `div` on `bg-background-secondary-surface` with a directional loading sweep (`--animate-skeleton-sweep`); `data-slot="skeleton"`.
14
+
15
+ ## API
16
+
17
+ | Prop | Type | Default | Notes |
18
+ | --- | --- | --- | --- |
19
+ | `className` | `string` | — | Sizing and shape overrides; all other div props pass through. |
20
+
21
+ ## Variants & sizes
22
+
23
+ No variants — shape comes entirely from `className`.
24
+
25
+ ```tsx
26
+ import { Skeleton } from "@trading-game/design-intelligence-layer"
27
+
28
+ <Skeleton className="h-4 w-40" /> {/* text line */}
29
+ <Skeleton className="size-10 rounded-full" /> {/* avatar */}
30
+ <Skeleton className="h-24 w-full rounded-2xl" /> {/* card */}
31
+ ```
32
+
33
+ ## Tokens
34
+
35
+ **Colour (semantic):** `bg-background-secondary-surface`.
36
+ **Type (private):** none.
37
+
38
+ ## Behaviour
39
+
40
+ - Purely presentational; the animation is the named `--animate-skeleton-sweep` token (1.6s directional gradient), replacing the generic pulse.
41
+ - Default radius is `rounded-md` (8px, the row radius); override to match the target shape — `rounded-full` for avatars, `rounded-2xl` for cards.
42
+
43
+ ## Do / Don't
44
+
45
+ **Do**
46
+ - Match the skeleton's radius to the component it stands in for (rows `rounded-md`, cards `rounded-2xl`, avatars circles).
47
+ - Mirror the real layout's dimensions so content does not jump on load.
48
+
49
+ **Don't**
50
+ - Don't tint skeletons with brand colour — they stay on the neutral secondary surface.
51
+ - Don't mix Skeleton and Spinner for the same region; pick one loading treatment.
@@ -0,0 +1,61 @@
1
+ # Slider
2
+
3
+ Continuous or stepped value selection on a track (Radix Slider), single- or multi-thumb.
4
+
5
+ ## When to use
6
+
7
+ - Picking a value or range along a bounded scale — leverage, risk percentage, price range filters.
8
+
9
+ **When not:** For exact numeric entry use [Stepper](./stepper.md) or Input. For on/off use [Switch](./switch.md).
10
+
11
+ ## Anatomy
12
+
13
+ - `Slider` — Radix root; renders track, range, and one thumb per value; `data-slot="slider"`.
14
+ - Track — `h-1.5` `rounded-full` bar on the secondary surface; `data-slot="slider-track"`.
15
+ - Range — filled portion, blue alpha glaze; `data-slot="slider-range"`.
16
+ - Thumb — `size-4` circle, solid brand fill; `data-slot="slider-thumb"`.
17
+
18
+ ## API
19
+
20
+ | Prop | Type | Default | Notes |
21
+ | --- | --- | --- | --- |
22
+ | `value` / `defaultValue` | `number[]` | — | Thumb count = array length; with neither set, two thumbs render at `[min, max]`. |
23
+ | `min` / `max` | `number` | `0` / `100` | Bounds. |
24
+ | `onValueChange` | `(value: number[]) => void` | — | Radix callback. |
25
+ | `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Vertical gets `min-h-44`. |
26
+ | `step`, `disabled`, ... | Radix `Slider.Root` props | — | Pass through; disabled renders at 50% opacity. |
27
+
28
+ ## Variants & sizes
29
+
30
+ No variants and no size prop.
31
+
32
+ ```tsx
33
+ import { Slider } from "@trading-game/design-intelligence-layer"
34
+
35
+ <Slider defaultValue={[25]} max={100} step={1} />
36
+ <Slider defaultValue={[20, 80]} /> {/* range, two thumbs */}
37
+ ```
38
+
39
+ ## Tokens
40
+
41
+ **Colour (semantic):** `bg-background-secondary-surface` (track), `bg-background-brand-default` (thumb), `ring-ring-focus-strong` (hover/focus ring, `ring-4`).
42
+ **Range fill:** solid `bg-background-brand-default` — the shared track spec with Progress (h-2, rounded-full, secondary-surface trackly, per the glaze exception to the semantic-only rule (a matching `--slider-range` alias exists in `styles.css`).
43
+ **Type (private):** none — the component renders no text.
44
+
45
+ ## Behaviour
46
+
47
+ - Thumb count is derived from the value array; an uncontrolled slider with no `defaultValue` renders two thumbs at `[min, max]`.
48
+ - The thumb is a `rounded-full size-4` brand circle with `border-0`; hover and keyboard focus both show a 4px `ring-ring-focus-strong` halo.
49
+ - Vertical orientation flips the layout (`flex-col`, `w-1.5` track) and enforces `min-h-44`.
50
+ - Disabled state: root and thumbs at `opacity-24`, pointer events off on thumbs.
51
+
52
+ ## Do / Don't
53
+
54
+ **Do**
55
+ - Pass an explicit `defaultValue`/`value` array — relying on the `[min, max]` fallback silently creates a two-thumb range slider.
56
+ - Pair the slider with a live numeric readout (or a [Stepper](./stepper.md)) for precise amounts.
57
+
58
+ **Don't**
59
+ - Don't recolour the range with an opaque brand fill — the 40% blue alpha glaze over the secondary track is the design.
60
+ - Don't resize the thumb; the `size-4` circle matches the circular-control rule.
61
+ - Don't add shadows to track or thumb.