@trading-game/design-intelligence-layer 0.17.4 → 1.0.1

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 +105 -231
  2. package/README.md +50 -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 +110 -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 +1634 -252
  77. package/guides/design-system-guide/trading-game-ds-guide.md +0 -933
@@ -0,0 +1,94 @@
1
+ # Button
2
+
3
+ The system's action pill — brand-filled by default, with surface, ghost, on-brand, and glass families plus loading and shimmer modifiers.
4
+
5
+ ## When to use
6
+
7
+ - Any tap-to-act control: submit, trade, confirm, navigate-as-action.
8
+
9
+ **When not:** For selectable amount/filter pills use [Chip](./chip.md). For read-only status use [Badge](./badge.md). For menu triggers prefer [Dropdown Menu](./dropdown-menu.md)'s trigger wrapping a Button.
10
+
11
+ ## Anatomy
12
+
13
+ - `Button` — single element (`button`, or `Slot` with `asChild`); `data-slot="button"`, plus `data-variant`, `data-size`, `data-loading`, `data-shimmer`.
14
+ - Shimmer layer — an absolutely positioned gradient sweep span, rendered under the label.
15
+ - Loading layer — children go invisible (width preserved), a centred `Spinner` overlays.
16
+ - `buttonVariants` — exported cva for composing button classes onto other elements (Calendar does this).
17
+
18
+ ## API
19
+
20
+ ### Button
21
+
22
+ | Prop | Type | Default | Notes |
23
+ | --- | --- | --- | --- |
24
+ | `variant` | `"primary" \| "secondary" \| "tertiary" \| "primary-on-brand" \| "secondary-on-brand" \| "tertiary-on-brand" \| "frosted" \| "frosted-on-brand"` | `"primary"` | See variants below. |
25
+ | `size` | `"sm" \| "md" \| "lg" \| "icon" \| "icon-xs" \| "icon-sm" \| "icon-md" \| "icon-lg"` | `"md"` | Text rail 32/40/48; icon circles 24/32/40/48 (`icon` = 48, same as `icon-lg`). |
26
+ | `loading` | `boolean` | `false` | Spinner overlay, `aria-busy`, pointer-events off, opacity 24%. |
27
+ | `shimmer` | `boolean` | `false` | Attention sweep; primary family only (`primary`, `primary-on-brand`) — ignored elsewhere with a dev warning. |
28
+ | `asChild` | `boolean` | `false` | Merge styling onto one child element. Shimmer and loading are unavailable in this mode (Slot takes exactly one child). |
29
+ | `variant` (deprecated) | `"primary-inverse" \| "primary-shimmer" \| "primary-on-brand-shimmer" \| "primary-inverse-shimmer" \| "secondary-inverse" \| "tertiary-inverse"` | — | @deprecated — removed at v1.0. `*-inverse` → the `*-on-brand` family; `*-shimmer` → the `shimmer` boolean. |
30
+
31
+ ## Variants & sizes
32
+
33
+ ```tsx
34
+ import { Button } from "@trading-game/design-intelligence-layer"
35
+
36
+ <Button>Trade now</Button>
37
+ <Button variant="secondary">Cancel</Button>
38
+ <Button variant="tertiary" size="sm">Details</Button>
39
+ <Button shimmer>Claim bonus</Button>
40
+ <Button loading>Placing order</Button>
41
+ <Button variant="frosted">On page surfaces</Button>
42
+ <Button variant="frosted-on-brand">On artwork</Button>
43
+ <Button variant="primary-on-brand">On a brand panel</Button>
44
+ <Button size="icon-md" aria-label="Settings"><SettingsIcon /></Button>
45
+ ```
46
+
47
+ | Variant | Surface | Ink |
48
+ | --- | --- | --- |
49
+ | `primary` | `background-brand-default`, hover `-hover`, press `-pressed` | `text-on-brand-static` |
50
+ | `secondary` | `background-primary-surface` + hairline border, hover `background-secondary-surface` | `text-prominent-default` |
51
+ | `tertiary` | transparent, hover `background-hover-default` | `text-prominent-default` |
52
+ | `primary-on-brand` | `background-static-white`, hover white/80 | `text-brand-default` |
53
+ | `secondary-on-brand` | transparent + white border, hover white-alpha-8 | `text-on-brand-static` |
54
+ | `tertiary-on-brand` | transparent, hover white-alpha-8 | `text-on-brand-static` |
55
+ | `frosted` | `background-brand-selected` tint; hover/pressed = the state-layer glazes over the solid fill | `text-brand-selected` |
56
+ | `frosted-on-brand` | white-alpha-24 frost + blur, hover 32, press 16 | `text-on-brand-static` |
57
+
58
+ Use `frosted` for quiet brand-tinted actions on page surfaces — stepper −/+ circles, quick utility buttons that shouldn't shout like `primary`. Use `frosted-on-brand` for the same job on art/hero/brand surfaces. One family, surface picks the name (`glass` is its @deprecated alias, deleted at v1.0).
59
+
60
+ | Size | Height | Padding-x | Type | Icon |
61
+ | --- | --- | --- | --- | --- |
62
+ | `sm` | 32px | 12px | 14/semibold/20 | 14px |
63
+ | `md` | 40px | 16px | 16/semibold/24 | 16px |
64
+ | `lg` | 48px | 20px | 18/semibold/24 | 20px |
65
+
66
+ Padding-x = height/2 − 4; gap fixed at 8px and padding does not change when icons are absent. Icon buttons are true circles: `icon-xs` 24, `icon-sm` 32, `icon-md` 40, `icon`/`icon-lg` 48.
67
+
68
+ ## Tokens
69
+
70
+ **Colour (semantic):** `background-brand-default/-hover/-pressed`, `text-on-brand-static`, `background-primary-surface`, `background-secondary-surface`, `border-border-default-default`, `text-prominent-default`, `background-hover-default`, `background-static-white`, `text-brand-default`, `border-error-default` + `ring-error-soft` (aria-invalid), `ring-focus-strong`; alpha primitives `--primitive-white-alpha-8/16/24/32` for on-brand/glass glazes only.
71
+ **Type (private):** `--button-font-size/line-height-{lg,md,sm}` = 18/24, 16/24, 14/20; `--button-font-weight` = semibold (600).
72
+
73
+ ## Behaviour
74
+
75
+ - All shapes are `rounded-full`; icon sizes are perfect circles.
76
+ - Focus: `focus-visible:ring-[3px] ring-ring-focus-strong` + focus border. Disabled: `opacity-24`, pointer-events off.
77
+ - Shimmer is a 4s looping gradient sweep — white over `primary`, brand-blue over `primary-on-brand` (white would vanish on white). It is suppressed while `loading` or `disabled` so the button reads as inert.
78
+ - `loading` keeps the button's width: children render invisible, the spinner centres on top, sized per button size.
79
+ - `asChild` passes children straight into Slot — the shimmer/loading layers physically cannot render there.
80
+ - Deprecated `*-shimmer` variants imply `shimmer` for back-compat; `*-inverse` names alias the on-brand styles. All deleted at v1.0.
81
+ - The `.type-button-*` classes are plain CSS and beat `font-*` utilities; the `active:opacity-60` press dim is replaced by the pressed fill on `primary` and by thinner frost on `glass`.
82
+
83
+ ## Do / Don't
84
+
85
+ **Do**
86
+ - Default to `md` (40px); reserve `lg` for the screen's single headline CTA.
87
+ - Use the on-brand family on brand/coloured panels — never the theme-dependent variants there.
88
+ - Give icon-only buttons an `aria-label`.
89
+
90
+ **Don't**
91
+ - Don't use the deprecated `*-inverse` / `*-shimmer` variants in new code — they are deleted at v1.0.
92
+ - Don't apply `shimmer` outside the primary family; it is ignored and warns.
93
+ - Don't override the pill radius or add shadows; buttons are flat, full-round pills.
94
+ - Don't change horizontal padding when hiding icons — the rail keeps 12/16/20 regardless.
@@ -0,0 +1,74 @@
1
+ # Calendar
2
+
3
+ Date and date-range picker built on react-day-picker, restyled with circular day cells and the system's themed dropdowns.
4
+
5
+ ## When to use
6
+
7
+ - Picking expiry dates, statement ranges, or tournament windows — single dates or ranges.
8
+
9
+ **When not:** For choosing from a short list of preset periods use [Chip](./chip.md) rows or a Select. Embed the calendar in a Popover for input-triggered pickers.
10
+
11
+ ## Anatomy
12
+
13
+ - `Calendar` — configured `DayPicker`; root gets `data-slot="calendar"`, panel is `w-fit`, `p-3`, cell size `--cell-size` = 32px. Background goes transparent inside `card-content` and `popover-content`.
14
+ - Nav — an absolute strip over the caption using `pointer-events-none`, with the arrow Buttons re-enabling their own hits so clicks pass through to the caption dropdowns beneath.
15
+ - `CalendarDropdown` (internal `components.Dropdown`) — themed Select replacing the native month/year `<select>`; popper-positioned panel, `max-h-64`.
16
+ - `CalendarDayButton` — exported day cell: a tertiary icon Button, circular, with today/selected/range modifiers via data attributes.
17
+
18
+ ## API
19
+
20
+ ### Calendar
21
+
22
+ | Prop | Type | Default | Notes |
23
+ | --- | --- | --- | --- |
24
+ | `showOutsideDays` | `boolean` | `true` | Outside days render in disabled ink. |
25
+ | `captionLayout` | DayPicker layout | `"label"` | `"dropdown"` swaps in the themed Selects. |
26
+ | `buttonVariant` | Button variant | `"secondary"` | Variant for the prev/next arrows. |
27
+ | `mode`, `selected`, `onSelect`, … | DayPicker props | — | Full react-day-picker API passes through, as do `classNames`, `components`, `formatters`. |
28
+
29
+ ### CalendarDayButton
30
+
31
+ Receives react-day-picker's `day` and `modifiers`; exposed for custom `components.DayButton` composition.
32
+
33
+ ## Variants & sizes
34
+
35
+ One visual variant; caption layout is the axis.
36
+
37
+ ```tsx
38
+ import { Calendar } from "@trading-game/design-intelligence-layer"
39
+
40
+ <Calendar mode="single" selected={date} onSelect={setDate} />
41
+ <Calendar mode="range" selected={range} onSelect={setRange} captionLayout="dropdown" />
42
+ ```
43
+
44
+ ## Tokens
45
+
46
+ **Colour (semantic):** `background-primary-surface` (panel), `text-text-prominent-default` (caption, days), `text-text-subtle-default` (weekdays, week numbers), `text-text-disabled-default` (outside/disabled days), `background-hover-default` (day hover glaze), `--background-brand-container` + `text-text-brand-container-static` (selected day and range band — the static icy recipe, painted via `[background:var(…)]`), `text-text-brand-default` + `background-brand-default` (today's number and dot), `icon-subtle-default` (dropdown chevron), `ring-focus-strong` (focus).
47
+ **Type (private):**
48
+
49
+ | Token | Value |
50
+ | --- | --- |
51
+ | `--calendar-caption-*` | 14 / semibold (600) / 20 |
52
+ | `--calendar-weekday-*` | 12 / medium (500) / 16 |
53
+ | `--calendar-day-*` | 14 / regular (400) / 20 |
54
+
55
+ ## Behaviour
56
+
57
+ - Day cells are circular (tertiary icon Buttons). Selection paints the static icy `--background-brand-container` via arbitrary `background` — a `bg-*` utility cannot override it — with `text-brand-container-static` ink and semibold numerals.
58
+ - Ranges: circular start/end (rounded-l/r-full, semibold), square middles (`rounded-none`), all on the same icy band; first/last cells in a row re-round via row selectors.
59
+ - Today (unselected) keeps the regular cell but takes brand ink, semibold, and a 4px brand dot centred below the numeral.
60
+ - `CalendarDropdown` replaces the native selects with the themed Select: `position="popper"` content so the panel scrolls in place, and fixed trigger widths — `w-20` for months, `w-24` for years — so the `w-fit` panel never resizes between "Jan" and "Sep" or 999 and 2026.
61
+ - The nav strip overlays the caption with `pointer-events-none`; only the arrow buttons (`pointer-events-auto`) catch clicks, so the dropdowns underneath stay clickable.
62
+ - Focused days are programmatically focused (`modifiers.focused` → `ref.focus()`), and RTL flips the nav chevrons.
63
+
64
+ ## Do / Don't
65
+
66
+ **Do**
67
+ - Use `captionLayout="dropdown"` for far-past/future dates (date of birth, statements).
68
+ - Keep the 32px `--cell-size`; resize via that variable, not per-cell classes.
69
+ - Host in a Popover or Card — the calendar background auto-clears there.
70
+
71
+ **Don't**
72
+ - Don't restyle selected days with `bg-*` utilities — the icy container is painted via `background` and will win.
73
+ - Don't theme the selection per colour scheme by hand; `--background-brand-container` is the static recipe on purpose.
74
+ - Don't square the day cells; circles (and square range middles) are the system shape.
@@ -0,0 +1,79 @@
1
+ # Card
2
+
3
+ The shared surface shell — card surface, hairline border, 18px radius, flat — with slotted header, content, and footer.
4
+
5
+ ## When to use
6
+
7
+ - Grouping related content on a page: positions, game tiles, stats, settings sections.
8
+ - `variant="frosted-on-brand"` for cards sitting over hero or game artwork.
9
+
10
+ **When not:** For modal content use [Dialog](./dialog.md). For a painted promotional panel use [Banner](./banner.md).
11
+
12
+ ## Anatomy
13
+
14
+ - `Card` — the shell: `rounded-2xl` (18px), `py-4`, `gap-4`, no horizontal padding of its own; `data-slot="card"`, `data-variant`.
15
+ - `CardHeader` — grid with `px-4`; becomes two-column when a `CardAction` is present; `data-slot="card-header"`.
16
+ - `CardTitle` — `.h6` foundation style; `data-slot="card-title"`.
17
+ - `CardDescription` — `.body-sm` in subtle ink; `data-slot="card-description"`.
18
+ - `CardAction` — top-right slot spanning both header rows; `data-slot="card-action"`.
19
+ - `CardContent` — `px-4` body; `data-slot="card-content"`.
20
+ - `CardFooter` — `px-4` flex row; `data-slot="card-footer"`.
21
+
22
+ ## API
23
+
24
+ ### Card
25
+
26
+ | Prop | Type | Default | Notes |
27
+ | --- | --- | --- | --- |
28
+ | `variant` | `"default" \| "frosted-on-brand"` | `"default"` | Glass = white-alpha-80 + blur-xl + white-alpha-64 hairline, static black ink, identical in both themes. |
29
+ | `interactive` | `boolean` | `false` | Desktop-only hover lift: `sm:hover:-translate-y-0.75`, disabled under `motion-reduce`. |
30
+
31
+ All slots take `div` props plus `className`.
32
+
33
+ ## Variants & sizes
34
+
35
+ No size prop — cards size to their grid.
36
+
37
+ ```tsx
38
+ import {
39
+ Card, CardHeader, CardTitle, CardDescription,
40
+ CardAction, CardContent, CardFooter, Button,
41
+ } from "@trading-game/design-intelligence-layer"
42
+
43
+ <Card>
44
+ <CardHeader>
45
+ <CardTitle>Open positions</CardTitle>
46
+ <CardDescription>3 active trades</CardDescription>
47
+ <CardAction><Button variant="tertiary" size="sm">View all</Button></CardAction>
48
+ </CardHeader>
49
+ <CardContent>…</CardContent>
50
+ <CardFooter>…</CardFooter>
51
+ </Card>
52
+
53
+ <Card variant="frosted-on-brand" interactive>…</Card>
54
+ ```
55
+
56
+ ## Tokens
57
+
58
+ **Colour (semantic):** `background-primary-surface` + `border-border-default-default` + `text-text-prominent-default` (default), `text-text-subtle-default` (description); glass uses alpha primitives `--primitive-white-alpha-80` (fill) and `--primitive-white-alpha-64` (hairline) with `text-static-black` — the sanctioned alpha-for-glass exception.
59
+ **Type (private):** none — the title and description ride the foundation `.h6` and `.body-sm` classes.
60
+
61
+ ## Behaviour
62
+
63
+ - Cards ship blank: the shell has vertical `py-4` only; every slot brings its own `px-4`. Content placed directly in `Card` without a slot will touch the edges.
64
+ - `CardHeader` switches to `grid-cols-[1fr_auto]` only when it contains a `data-slot="card-action"` child.
65
+ - Adding utility classes `.border-b` on the header / `.border-t` on the footer also adds the matching `pb-4` / `pt-4`.
66
+ - `interactive` lifts 3px on hover at `sm+` only — no lift on touch; `motion-reduce` turns it off entirely.
67
+ - Glass is theme-independent by nature: same frost in light and dark.
68
+
69
+ ## Do / Don't
70
+
71
+ **Do**
72
+ - Use the slots — they carry the 16px gutters.
73
+ - Reserve `interactive` for cards that are themselves a single click target.
74
+ - Put glass cards only over imagery; on plain surfaces use the default.
75
+
76
+ **Don't**
77
+ - Don't add shadows — flat surfaces with hairlines are the elevation model.
78
+ - Don't change the 18px (`rounded-2xl`) radius; cards and dialogs share it.
79
+ - Don't use alpha fills on default cards; solids only — alpha is reserved for the glass variant.
@@ -0,0 +1,82 @@
1
+ # Carousel
2
+
3
+ Embla-powered slide track with previous/next controls, horizontal or vertical.
4
+
5
+ ## When to use
6
+
7
+ - Browsing peer content that overflows the viewport: game tiles, promo cards, market highlights.
8
+
9
+ **When not:** For content the user must compare side by side, use a grid. For sequential steps use Stepper.
10
+
11
+ ## Anatomy
12
+
13
+ - `Carousel` — context provider + `role="region"` wrapper with arrow-key handling; `data-slot="carousel"`.
14
+ - `CarouselContent` — the overflow-hidden viewport and flex track (`-ml-4` / `-mt-4` gutter offset); `data-slot="carousel-content"`.
15
+ - `CarouselItem` — one slide, `basis-full` with `pl-4` / `pt-4` gutter, `role="group" aria-roledescription="slide"`; `data-slot="carousel-item"`.
16
+ - `CarouselPrevious` / `CarouselNext` — circular icon Buttons positioned outside the track (−48px on the travel axis); `data-slot="carousel-previous"` / `"carousel-next"`.
17
+ - `CarouselApi` — exported Embla API type for `setApi`.
18
+
19
+ ## API
20
+
21
+ ### Carousel
22
+
23
+ | Prop | Type | Default | Notes |
24
+ | --- | --- | --- | --- |
25
+ | `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Sets the Embla axis. |
26
+ | `opts` | `EmblaOptionsType` | — | Passed straight to Embla (`loop`, `align`, …). |
27
+ | `plugins` | Embla plugins | — | e.g. autoplay. |
28
+ | `setApi` | `(api: CarouselApi) => void` | — | Escape hatch to the Embla instance. |
29
+
30
+ ### CarouselPrevious / CarouselNext
31
+
32
+ | Prop | Type | Default | Notes |
33
+ | --- | --- | --- | --- |
34
+ | `variant` | Button variant | `"secondary"` | Any [Button](./button.md) variant. |
35
+ | `size` | Button size | `"icon-md"` | 40px circle. |
36
+
37
+ ## Variants & sizes
38
+
39
+ Slide width is set on the item, not by a prop.
40
+
41
+ ```tsx
42
+ import {
43
+ Carousel, CarouselContent, CarouselItem,
44
+ CarouselPrevious, CarouselNext,
45
+ } from "@trading-game/design-intelligence-layer"
46
+
47
+ <Carousel opts={{ align: "start", loop: true }}>
48
+ <CarouselContent>
49
+ <CarouselItem className="basis-1/3">…</CarouselItem>
50
+ <CarouselItem className="basis-1/3">…</CarouselItem>
51
+ <CarouselItem className="basis-1/3">…</CarouselItem>
52
+ </CarouselContent>
53
+ <CarouselPrevious />
54
+ <CarouselNext />
55
+ </Carousel>
56
+ ```
57
+
58
+ ## Tokens
59
+
60
+ **Colour (semantic):** none of its own — the arrows inherit the secondary Button tokens (`background-primary-surface`, `border-border-default-default`, `text-prominent-default`); the track paints nothing.
61
+ **Type (private):** none.
62
+
63
+ ## Behaviour
64
+
65
+ - Any part outside `<Carousel>` throws (`useCarousel must be used within a <Carousel />`).
66
+ - Arrow buttons disable themselves at the ends (`canScrollPrev/Next` from Embla `select`/`reInit` events); with `loop: true` they never disable.
67
+ - ArrowLeft/ArrowRight are captured on the wrapper and scroll the track.
68
+ - Vertical orientation rotates the arrows 90° and places them above/below the track.
69
+ - Slide gutter is a fixed 16px built from the `-ml-4`/`pl-4` pair — adjust both together if you must change it.
70
+ - `setApi` exposes the raw Embla instance for dots, autoplay, or scroll-to.
71
+
72
+ ## Do / Don't
73
+
74
+ **Do**
75
+ - Set slide width with `basis-*` on `CarouselItem` (`basis-1/3`, `md:basis-1/4`).
76
+ - Keep the default `secondary` / `icon-md` arrows — 40px circles on the size rail.
77
+ - Reposition the absolute arrows when the carousel sits flush to a container edge.
78
+
79
+ **Don't**
80
+ - Don't hide the arrows without providing another affordance (drag alone is not discoverable on desktop).
81
+ - Don't restyle the arrows as squares; icon buttons are circles system-wide.
82
+ - Don't nest a carousel inside another carousel on the same axis.
@@ -0,0 +1,66 @@
1
+ # Checkbox
2
+
3
+ Binary (or indeterminate) form control that fills brand blue when checked.
4
+
5
+ ## When to use
6
+
7
+ - Multi-select lists, opt-ins, terms acceptance — anywhere several options can be on at once.
8
+
9
+ **When not:** For a single on/off setting with immediate effect use Switch. For mutually exclusive options use Radio Group. For pill-shaped multi-select filters use [Chip](./chip.md).
10
+
11
+ ## Anatomy
12
+
13
+ - `Checkbox` — the only export; Radix root, 16px square, `rounded-2xs`; `data-slot="checkbox"`.
14
+ - Indicator — internal `CheckboxPrimitive.Indicator` with a 14px `CheckIcon`; `data-slot="checkbox-indicator"`.
15
+
16
+ ## API
17
+
18
+ ### Checkbox
19
+
20
+ | Prop | Type | Default | Notes |
21
+ | --- | --- | --- | --- |
22
+ | `checked` | `boolean \| "indeterminate"` | — | Controlled state. |
23
+ | `defaultChecked` | `boolean` | — | Uncontrolled initial state. |
24
+ | `onCheckedChange` | `(checked) => void` | — | Radix change callback. |
25
+ | `disabled` | `boolean` | `false` | `cursor-not-allowed`, opacity 50%. |
26
+ | `required`, `name`, `value` | form props | — | Radix renders a hidden input inside forms. |
27
+
28
+ ## Variants & sizes
29
+
30
+ One variant, one size (16px).
31
+
32
+ ```tsx
33
+ import { Checkbox } from "@trading-game/design-intelligence-layer"
34
+
35
+ <label className="flex items-center gap-2">
36
+ <Checkbox defaultChecked />
37
+ Accept the terms
38
+ </label>
39
+
40
+ <Checkbox checked={all ? true : some ? "indeterminate" : false} onCheckedChange={toggleAll} />
41
+ ```
42
+
43
+ ## Tokens
44
+
45
+ **Colour (semantic):** `background-primary-surface` + `border-border-default-default` (unchecked), `background-brand-default` + `text-text-on-brand-static` with transparent border (checked), `ring-focus-strong` + `border-ring-focus-default` (focus).
46
+ **Type (private):** none — the control renders no text.
47
+
48
+ ## Behaviour
49
+
50
+ - Checked swaps the hairline for a solid brand fill (`data-[state=checked]`); the check glyph rides `text-current`, so it is static white ink.
51
+ - Works controlled or uncontrolled via Radix; supports `"indeterminate"`.
52
+ - The root carries `peer`, so labels/siblings can react with `peer-*` selectors.
53
+ - Radius is `rounded-2xs` — one step below the 4px input radius, deliberate at this 16px scale.
54
+ - Focus is the standard control ring: `focus-visible:ring-[3px] ring-ring-focus-strong`.
55
+
56
+ ## Do / Don't
57
+
58
+ **Do**
59
+ - Wrap in a `<label>` or pair with `htmlFor` — the 16px box alone is too small a target.
60
+ - Use `"indeterminate"` for parent rows over partially selected children.
61
+ - Keep the brand fill; checked state is solid, not tinted.
62
+
63
+ **Don't**
64
+ - Don't scale the box past 16px with `className` size hacks — layout assumes `size-4`.
65
+ - Don't restyle checked with selection-state tokens; checkbox uses the brand fill, not the brand-selected family.
66
+ - Don't use a checkbox where the change applies instantly — that is Switch.
@@ -0,0 +1,72 @@
1
+ # Chip
2
+
3
+ Pressable pill for quick amounts and category filters — one shape, four jobs: default, selected, outline (error recovery), disabled.
4
+
5
+ ## When to use
6
+
7
+ - Quick-amount rows (10.00 / 50.00 / 100.00), category filters, and the "Max 1,240.50" recovery chip after an over-limit entry.
8
+
9
+ **When not:** Live-status pills belong to [Badge](./badge.md). Actions that submit or navigate belong to [Button](./button.md).
10
+
11
+ ## Anatomy
12
+
13
+ - `Chip` — a single `<button type="button">`; `data-slot="chip"`, `data-size`, `data-variant`, `data-selected`, `aria-pressed`.
14
+ - `chipVariants` — exported cva (size axis only) for composition.
15
+
16
+ ## API
17
+
18
+ ### Chip
19
+
20
+ | Prop | Type | Default | Notes |
21
+ | --- | --- | --- | --- |
22
+ | `selected` | `boolean` | `false` | Solid brand fill + static white ink; the chip becomes inert (`pointer-events-none`). |
23
+ | `variant` | `"default" \| "outline"` | `"default"` | `outline` = the brand-outlined error-recovery chip. |
24
+ | `size` | `"md" \| "sm"` | `"md"` | 40px / 32px heights. |
25
+ | `disabled` | `boolean` | `false` | Hairline border, transparent fill, disabled ink. |
26
+
27
+ ## Variants & sizes
28
+
29
+ ```tsx
30
+ import { Chip } from "@trading-game/design-intelligence-layer"
31
+
32
+ <Chip onClick={() => setAmount(50)}>50.00</Chip>
33
+ <Chip selected>100.00</Chip>
34
+ <Chip variant="outline" onClick={setMax}>Max 1,240.50</Chip>
35
+ <Chip size="sm">Crypto</Chip>
36
+ <Chip disabled>500.00</Chip>
37
+ ```
38
+
39
+ | Size | Height | Padding-x | Type |
40
+ | --- | --- | --- | --- |
41
+ | `md` | 40px | 16px | 14/semibold/20 |
42
+ | `sm` | 32px | 12px | 12/semibold/16 |
43
+
44
+ ## Tokens
45
+
46
+ **Colour (semantic):**
47
+ - Default: `background-primary-surface` + `border-border-default-default` + `text-prominent-default`; hover `background-secondary-surface`; active `background-secondary-surface/80` glaze.
48
+ - Selected: `background-brand-default` + `text-on-brand-static`, transparent border.
49
+ - Outline: 1.5px `border-border-brand-selected` + `text-text-brand-selected` on `background-primary-surface`; hover `background-hover-default`.
50
+ - Disabled: `border-border-default-default`, transparent fill, `text-disabled-default`.
51
+ - Focus: `ring-focus-strong`.
52
+
53
+ **Type (private):** `--chip-font-size/line-height-md` = 14/20, `--chip-font-size/line-height-sm` = 12/16, `--chip-font-weight` = semibold (600).
54
+
55
+ ## Behaviour
56
+
57
+ - `selected` chips are intentionally inert: `pointer-events-none cursor-default` — re-tapping the chosen amount does nothing; selection changes by pressing a different chip. `aria-pressed` reflects the state.
58
+ - The outline variant is the error-recovery affordance — it uses the brand-selected token family (1.5px `border-brand-selected` + `text-brand-selected`), so in dark theme it takes the family's own values (blue-300 border, blue-200 ink) rather than a mirror of light.
59
+ - Always renders `type="button"`, so it never submits forms accidentally.
60
+ - Shape is `rounded-full`; there is no icon slot in the API.
61
+
62
+ ## Do / Don't
63
+
64
+ **Do**
65
+ - Use `md` (40px) in amount trays; `sm` (32px) for dense filter rows.
66
+ - Reserve `variant="outline"` for recovery actions like "Max …" after a validation error.
67
+ - Drive selection from state — exactly one selected chip per exclusive group.
68
+
69
+ **Don't**
70
+ - Don't attach `onClick` behaviour that depends on pressing a selected chip — it cannot receive clicks.
71
+ - Don't use Chip for statuses or counts; that is Badge.
72
+ - Don't exceed semibold 600 or swap the pill radius.
@@ -0,0 +1,86 @@
1
+ # Command
2
+
3
+ Filterable command palette built on cmdk — a searchable list of actions, standalone or inside a dialog.
4
+
5
+ ## When to use
6
+
7
+ - Keyboard-first action launchers (⌘K palettes) and searchable pickers over many options.
8
+
9
+ **When not:** For a short static action list anchored to a trigger use [Dropdown Menu](./dropdown-menu.md). For right-click actions use [Context Menu](./context-menu.md).
10
+
11
+ ## Anatomy
12
+
13
+ - `Command` — cmdk root, `rounded-2xl` surface shell; `data-slot="command"`.
14
+ - `CommandDialog` — the palette wrapped in a [Dialog](./dialog.md) with an sr-only header and `p-0` content; resizes rows/input to 48px.
15
+ - `CommandInput` — search field inside a bordered wrapper row (36px standalone, 48px in the dialog) with a leading 16px search icon; `data-slot="command-input"` / `"command-input-wrapper"`.
16
+ - `CommandList` — scrollable results, `max-h-[300px]`; `data-slot="command-list"`.
17
+ - `CommandEmpty` — centred no-results row; `data-slot="command-empty"`.
18
+ - `CommandGroup` — group with a `cmdk-group-heading`; `data-slot="command-group"`.
19
+ - `CommandItem` — result row, `rounded-md`, 16px icons in subtle ink; `data-slot="command-item"`.
20
+ - `CommandShortcut` — right-aligned shortcut hint; `data-slot="command-shortcut"`.
21
+ - `CommandSeparator` — 1px hairline; `data-slot="command-separator"`.
22
+
23
+ ## API
24
+
25
+ ### CommandDialog
26
+
27
+ | Prop | Type | Default | Notes |
28
+ | --- | --- | --- | --- |
29
+ | `title` | `string` | `"Command Palette"` | sr-only dialog title. |
30
+ | `description` | `string` | `"Search for a command to run..."` | sr-only description. |
31
+ | `showCloseButton` | `boolean` | `true` | Dialog's circular close X. |
32
+ | `open`, `onOpenChange` | Dialog props | — | Pass through to Dialog. |
33
+
34
+ ### Others
35
+
36
+ `Command`, `CommandInput`, `CommandList`, `CommandEmpty`, `CommandGroup`, `CommandItem`, `CommandSeparator` take the cmdk primitive props (`value`, `onSelect`, `disabled`, `filter`, `shouldFilter`, …) plus `className`.
37
+
38
+ ## Variants & sizes
39
+
40
+ One look; standalone vs dialog is the axis.
41
+
42
+ ```tsx
43
+ import {
44
+ CommandDialog, CommandInput, CommandList, CommandEmpty,
45
+ CommandGroup, CommandItem, CommandShortcut, CommandSeparator,
46
+ } from "@trading-game/design-intelligence-layer"
47
+
48
+ <CommandDialog open={open} onOpenChange={setOpen}>
49
+ <CommandInput placeholder="Search markets…" />
50
+ <CommandList>
51
+ <CommandEmpty>No results.</CommandEmpty>
52
+ <CommandGroup heading="Markets">
53
+ <CommandItem onSelect={() => go("/btc")}>BTC/USD<CommandShortcut>⌘1</CommandShortcut></CommandItem>
54
+ </CommandGroup>
55
+ <CommandSeparator />
56
+ <CommandGroup heading="Actions">
57
+ <CommandItem>New trade</CommandItem>
58
+ </CommandGroup>
59
+ </CommandList>
60
+ </CommandDialog>
61
+ ```
62
+
63
+ ## Tokens
64
+
65
+ **Colour (semantic):** `background-primary-surface` (shell), `text-prominent-default` (rows), `text-subtle-default` (placeholder, headings, empty, shortcuts), `border-border-default-default` (input hairline, separator), `background-hover-default` (highlighted row via `data-[selected=true]`), `icon-subtle-default` (search and row icons).
66
+ **Type (private):** shared `--menu-*` family — item 14/regular/20, heading 12/medium/16, trigger 14/medium/20, shortcut 12/regular/16. Input and empty ride `type-menu-item`; headings and shortcuts ride `type-menu-heading`.
67
+
68
+ ## Behaviour
69
+
70
+ - cmdk filters as you type and moves the highlight (`data-[selected=true]` = the hover glaze); Enter fires `onSelect`.
71
+ - `CommandDialog` hides its title/description for screen readers only, strips content padding, and upsizes input and rows to 48px with 20px icons.
72
+ - Rows are `rounded-md` (8px) per the menu-row radius rule; the shell is `rounded-2xl` standalone (the dialog supplies its own 18px shell).
73
+ - Disabled items: `data-[disabled=true]:pointer-events-none opacity-24`.
74
+ - Note the shell radius difference from anchored menus: Command is a modal/embedded surface (18px), while dropdown/context panels are `rounded-lg` (10px).
75
+
76
+ ## Do / Don't
77
+
78
+ **Do**
79
+ - Always include `CommandEmpty` so filtering dead-ends read intentionally.
80
+ - Group with headings once the list passes ~7 items.
81
+ - Keep shortcut hints in `CommandShortcut` — 12/regular/16, right-aligned.
82
+
83
+ **Don't**
84
+ - Don't restyle the highlight with selection-state tokens — palette highlight is the hover glaze, not brand-selected.
85
+ - Don't put form controls inside items; rows are single actions.
86
+ - Don't cap `CommandList` taller than 300px without a reason — it is scrollable by design.
@@ -0,0 +1,90 @@
1
+ # Context Menu
2
+
3
+ Right-click (or long-press) menu with items, checkboxes, radios, labels, and nested submenus.
4
+
5
+ ## When to use
6
+
7
+ - Contextual actions on an object under the pointer: a watchlist row, a chart, a portfolio position.
8
+
9
+ **When not:** For a menu opened from a visible trigger use [Dropdown Menu](./dropdown-menu.md). For a searchable action list use [Command](./command.md).
10
+
11
+ ## Anatomy
12
+
13
+ - `ContextMenu` / `ContextMenuTrigger` / `ContextMenuPortal` / `ContextMenuGroup` / `ContextMenuRadioGroup` / `ContextMenuSub` — Radix plumbing, each stamped with its `data-slot` (`context-menu`, `context-menu-trigger`, …).
14
+ - `ContextMenuContent` / `ContextMenuSubContent` — the panel: `rounded-lg` (10px), hairline border, surface fill, `p-1`, `min-w-[8rem]`; content scrolls within the Radix available height.
15
+ - `ContextMenuItem` — action row, `rounded-md` (8px); `data-slot="context-menu-item"`, `data-inset`, `data-variant`.
16
+ - `ContextMenuCheckboxItem` / `ContextMenuRadioItem` — rows with `pl-8` and a left indicator (16px check / 8px filled circle).
17
+ - `ContextMenuLabel` — non-interactive heading; `data-slot="context-menu-label"`.
18
+ - `ContextMenuSeparator` — 1px hairline; `data-slot="context-menu-separator"`.
19
+ - `ContextMenuShortcut` — right-aligned hint; `data-slot="context-menu-shortcut"`.
20
+ - `ContextMenuSubTrigger` — row with trailing chevron; `data-slot="context-menu-sub-trigger"`.
21
+
22
+ ## API
23
+
24
+ ### ContextMenuItem
25
+
26
+ | Prop | Type | Default | Notes |
27
+ | --- | --- | --- | --- |
28
+ | `variant` | `"default" \| "destructive"` | `"default"` | Destructive = error ink; focus paints the error tint surface. |
29
+ | `inset` | `boolean` | — | `pl-8` to align with indicator rows. |
30
+ | `onSelect`, `disabled` | Radix props | — | Standard item behaviour. |
31
+
32
+ ### ContextMenuCheckboxItem / ContextMenuRadioItem
33
+
34
+ | Prop | Type | Default | Notes |
35
+ | --- | --- | --- | --- |
36
+ | `checked` | `boolean \| "indeterminate"` | — | Checkbox item only. |
37
+ | `value` | `string` | — | Radio item, within `ContextMenuRadioGroup`. |
38
+
39
+ `ContextMenuSubTrigger` and `ContextMenuLabel` also take `inset`.
40
+
41
+ ## Variants & sizes
42
+
43
+ One panel size; rows vary by kind.
44
+
45
+ ```tsx
46
+ import {
47
+ ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem,
48
+ ContextMenuCheckboxItem, ContextMenuSeparator, ContextMenuShortcut,
49
+ ContextMenuSub, ContextMenuSubTrigger, ContextMenuSubContent,
50
+ } from "@trading-game/design-intelligence-layer"
51
+
52
+ <ContextMenu>
53
+ <ContextMenuTrigger>Right-click a position</ContextMenuTrigger>
54
+ <ContextMenuContent>
55
+ <ContextMenuItem>Set alert<ContextMenuShortcut>⌘A</ContextMenuShortcut></ContextMenuItem>
56
+ <ContextMenuCheckboxItem checked>Pin to top</ContextMenuCheckboxItem>
57
+ <ContextMenuSub>
58
+ <ContextMenuSubTrigger>Move to list</ContextMenuSubTrigger>
59
+ <ContextMenuSubContent><ContextMenuItem>Favourites</ContextMenuItem></ContextMenuSubContent>
60
+ </ContextMenuSub>
61
+ <ContextMenuSeparator />
62
+ <ContextMenuItem variant="destructive">Close position</ContextMenuItem>
63
+ </ContextMenuContent>
64
+ </ContextMenu>
65
+ ```
66
+
67
+ ## Tokens
68
+
69
+ **Colour (semantic):** `background-primary-surface` + `border-border-default-default` (panel), `text-prominent-default` (rows, labels), `background-hover-default` (focused/open row), `icon-subtle-default` (row icons), `text-subtle-default` (shortcuts), `text-error-default` + `background-error-default` (destructive row and its focus tint), `bg-border-default-default` (separator).
70
+ **Type (private):** shared `--menu-*` family — item 14/regular/20, heading 12/medium/16, trigger 14/medium/20, shortcut 12/regular/16.
71
+
72
+ ## Behaviour
73
+
74
+ - Open state, positioning, typeahead, and keyboard navigation are Radix-managed; content mounts in a portal with fade/zoom/slide animations per side.
75
+ - Focused rows take the hover glaze (`focus:bg-background-hover-default`) — not a brand-selected fill; an open sub-trigger holds the same glaze.
76
+ - Destructive rows keep error ink even on their icons (`*:[svg]:text-text-error-default!`).
77
+ - Disabled rows: `data-[disabled]:pointer-events-none opacity-24`.
78
+ - Radius contract: panel `rounded-lg` 10px, rows `rounded-md` 8px.
79
+
80
+ ## Do / Don't
81
+
82
+ **Do**
83
+ - Use `inset` on plain items that sit beside checkbox/radio rows so labels align.
84
+ - Put the destructive action last, after a separator.
85
+ - Keep row icons at the default 16px subtle ink.
86
+
87
+ **Don't**
88
+ - Don't use a context menu as the only way to reach an action — right-click is undiscoverable on touch.
89
+ - Don't restyle row focus with brand-selected tokens; menus highlight with the hover glaze.
90
+ - Don't nest submenus more than one level deep.