@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.
- package/AGENTS.md +104 -231
- package/README.md +46 -746
- package/dist/index.cjs +2753 -2986
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +247 -261
- package/dist/index.d.ts +247 -261
- package/dist/index.js +2648 -2856
- package/dist/index.js.map +1 -1
- package/docs/components/accordion.md +85 -0
- package/docs/components/alert-dialog.md +98 -0
- package/docs/components/aspect-ratio.md +55 -0
- package/docs/components/avatar.md +88 -0
- package/docs/components/badge.md +75 -0
- package/docs/components/banner.md +84 -0
- package/docs/components/bottom-navigation.md +90 -0
- package/docs/components/breadcrumb.md +85 -0
- package/docs/components/button.md +94 -0
- package/docs/components/calendar.md +74 -0
- package/docs/components/card.md +79 -0
- package/docs/components/carousel.md +82 -0
- package/docs/components/checkbox.md +66 -0
- package/docs/components/chip.md +72 -0
- package/docs/components/command.md +86 -0
- package/docs/components/context-menu.md +90 -0
- package/docs/components/dialog.md +95 -0
- package/docs/components/drawer.md +97 -0
- package/docs/components/dropdown-menu.md +92 -0
- package/docs/components/empty.md +87 -0
- package/docs/components/field.md +117 -0
- package/docs/components/hover-card.md +77 -0
- package/docs/components/input-group.md +105 -0
- package/docs/components/input-otp.md +87 -0
- package/docs/components/input.md +71 -0
- package/docs/components/item.md +105 -0
- package/docs/components/label.md +56 -0
- package/docs/components/link.md +66 -0
- package/docs/components/menubar.md +102 -0
- package/docs/components/native-select.md +71 -0
- package/docs/components/navigation-button.md +68 -0
- package/docs/components/navigation-menu.md +99 -0
- package/docs/components/numpad.md +78 -0
- package/docs/components/pagination.md +84 -0
- package/docs/components/popover.md +89 -0
- package/docs/components/profile-photo.md +81 -0
- package/docs/components/progress.md +60 -0
- package/docs/components/radio-group.md +82 -0
- package/docs/components/resizable.md +79 -0
- package/docs/components/scroll-area.md +66 -0
- package/docs/components/section-message.md +99 -0
- package/docs/components/select.md +105 -0
- package/docs/components/separator.md +55 -0
- package/docs/components/sheet.md +91 -0
- package/docs/components/sidebar.md +125 -0
- package/docs/components/skeleton.md +51 -0
- package/docs/components/slider.md +61 -0
- package/docs/components/spinner.md +52 -0
- package/docs/components/stepper.md +68 -0
- package/docs/components/switch.md +57 -0
- package/docs/components/table.md +86 -0
- package/docs/components/tabs.md +95 -0
- package/docs/components/textarea.md +58 -0
- package/docs/components/toast.md +66 -0
- package/docs/components/toggle-group.md +77 -0
- package/docs/components/toggle.md +60 -0
- package/docs/components/tooltip.md +83 -0
- package/docs/foundations/colors.md +109 -0
- package/docs/foundations/motion.md +63 -0
- package/docs/foundations/shape-layout.md +56 -0
- package/docs/foundations/typography.md +83 -0
- package/docs/patterns/forms.md +70 -0
- package/docs/patterns/menus.md +50 -0
- package/docs/patterns/on-brand.md +43 -0
- package/guides/audits/design-system-audit-2026-07.md +135 -0
- package/guides/rules/design-system-consuming-project.mdc +56 -0
- package/package.json +5 -6
- package/src/styles.css +1616 -251
- package/guides/design-system-guide/trading-game-ds-guide.md +0 -933
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Dialog
|
|
2
|
+
|
|
3
|
+
General-purpose modal on the shared card shell — dismissable, with an optional circular close button.
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
- Focused tasks and forms that interrupt the page: edit a watchlist, review order details, settings sub-flows.
|
|
8
|
+
|
|
9
|
+
**When not:** For destructive confirmations that must not be casually dismissed use [Alert Dialog](./alert-dialog.md). On mobile, edge-anchored flows belong to [Drawer](./drawer.md).
|
|
10
|
+
|
|
11
|
+
## Anatomy
|
|
12
|
+
|
|
13
|
+
- `Dialog` / `DialogTrigger` / `DialogPortal` / `DialogClose` — Radix plumbing with `data-slot` names (`dialog`, `dialog-trigger`, `dialog-portal`, `dialog-close`).
|
|
14
|
+
- `DialogOverlay` — scrim in `background-overlay-default`; `data-slot="dialog-overlay"`.
|
|
15
|
+
- `DialogContent` — centred card: `rounded-2xl` (18px), hairline border, `p-6`, `gap-6`, `sm:max-w-lg`, no shadow; `data-slot="dialog-content"`. Includes the close X unless disabled.
|
|
16
|
+
- Close X — 32px circle, top-right, subtle icon at 70% opacity, hover = full opacity + `background-hover-default` glaze.
|
|
17
|
+
- `DialogHeader` — column, centred text, left at `sm`; `data-slot="dialog-header"`.
|
|
18
|
+
- `DialogTitle` / `DialogDescription` — `type-dialog-title` / `-description`.
|
|
19
|
+
- `DialogFooter` — reverse-stacked buttons, row right-aligned at `sm`; can append its own Close button; `data-slot="dialog-footer"`.
|
|
20
|
+
|
|
21
|
+
## API
|
|
22
|
+
|
|
23
|
+
### Dialog (root)
|
|
24
|
+
|
|
25
|
+
| Prop | Type | Default | Notes |
|
|
26
|
+
| --- | --- | --- | --- |
|
|
27
|
+
| `open` / `defaultOpen` | `boolean` | — | Controlled / uncontrolled. |
|
|
28
|
+
| `onOpenChange` | `(open) => void` | — | Radix callback. |
|
|
29
|
+
| `modal` | `boolean` | `true` | Radix modality. |
|
|
30
|
+
|
|
31
|
+
### DialogContent
|
|
32
|
+
|
|
33
|
+
| Prop | Type | Default | Notes |
|
|
34
|
+
| --- | --- | --- | --- |
|
|
35
|
+
| `showCloseButton` | `boolean` | `true` | Renders the circular top-right X. |
|
|
36
|
+
|
|
37
|
+
### DialogFooter
|
|
38
|
+
|
|
39
|
+
| Prop | Type | Default | Notes |
|
|
40
|
+
| --- | --- | --- | --- |
|
|
41
|
+
| `showCloseButton` | `boolean` | `false` | Appends a `secondary` md Close button wired to `DialogPrimitive.Close`. |
|
|
42
|
+
|
|
43
|
+
## Variants & sizes
|
|
44
|
+
|
|
45
|
+
One size (`sm:max-w-lg`, `max-w-[calc(100%-2rem)]` on small screens); override via `className`.
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import {
|
|
49
|
+
Dialog, DialogTrigger, DialogContent, DialogHeader,
|
|
50
|
+
DialogTitle, DialogDescription, DialogFooter, Button,
|
|
51
|
+
} from "@trading-game/design-intelligence-layer"
|
|
52
|
+
|
|
53
|
+
<Dialog>
|
|
54
|
+
<DialogTrigger asChild><Button variant="secondary">Order details</Button></DialogTrigger>
|
|
55
|
+
<DialogContent>
|
|
56
|
+
<DialogHeader>
|
|
57
|
+
<DialogTitle>Order details</DialogTitle>
|
|
58
|
+
<DialogDescription>Filled 2 Sep 2026, 14:02.</DialogDescription>
|
|
59
|
+
</DialogHeader>
|
|
60
|
+
<DialogFooter showCloseButton>
|
|
61
|
+
<Button>Download receipt</Button>
|
|
62
|
+
</DialogFooter>
|
|
63
|
+
</DialogContent>
|
|
64
|
+
</Dialog>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Tokens
|
|
68
|
+
|
|
69
|
+
**Colour (semantic):** `background-overlay-default` (scrim), `background-primary-surface` (card), `border-border-default-default` (hairline), `text-prominent-default` (title), `text-subtle-default` (description), `icon-subtle-default` + `background-hover-default` (close X), `ring-focus-strong` (focus).
|
|
70
|
+
**Type (private):**
|
|
71
|
+
|
|
72
|
+
| Token | Value |
|
|
73
|
+
| --- | --- |
|
|
74
|
+
| `--dialog-title-*` | 18 / semibold (600) / 24 |
|
|
75
|
+
| `--dialog-description-*` | 14 / regular (400) / 20 |
|
|
76
|
+
|
|
77
|
+
## Behaviour
|
|
78
|
+
|
|
79
|
+
- Radix handles focus trap, Escape, and overlay dismissal; open/close animates fade + 95% zoom over `duration-base`.
|
|
80
|
+
- The close X is circular (`rounded-full`, 32px) with the hover glaze — the icon-button rule applied to a built-in control; it stops rendering with `showCloseButton={false}`.
|
|
81
|
+
- `DialogFooter showCloseButton` gives you a standard Cancel/Close without wiring `DialogClose` manually.
|
|
82
|
+
- The shell is flat: no shadow, `rounded-2xl` 18px, matching [Card](./card.md).
|
|
83
|
+
- `CommandDialog` composes this component with `p-0` content.
|
|
84
|
+
|
|
85
|
+
## Do / Don't
|
|
86
|
+
|
|
87
|
+
**Do**
|
|
88
|
+
- Keep titles at 18/semibold/24 — semibold is the maximum weight.
|
|
89
|
+
- Use `DialogTrigger asChild` around a Button rather than a bare trigger.
|
|
90
|
+
- Turn off the X (`showCloseButton={false}`) only when the footer offers an explicit exit.
|
|
91
|
+
|
|
92
|
+
**Don't**
|
|
93
|
+
- Don't add shadows or change the 18px radius.
|
|
94
|
+
- Don't use Dialog for irreversible confirmations — Alert Dialog exists so overlay-dismiss cannot destroy work.
|
|
95
|
+
- Don't stack dialogs on top of dialogs.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Drawer
|
|
2
|
+
|
|
3
|
+
Edge-anchored panel built on vaul — the mobile-first modal that slides from the bottom (or any edge) with a drag-to-dismiss handle.
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
- Mobile flows: amount entry, filters, detail sheets — anything a thumb should be able to swipe away.
|
|
8
|
+
- Side directions for narrow desktop panels when Sheet-style behaviour is wanted with drag physics.
|
|
9
|
+
|
|
10
|
+
**When not:** Centred desktop tasks belong to [Dialog](./dialog.md); hard confirmations to [Alert Dialog](./alert-dialog.md).
|
|
11
|
+
|
|
12
|
+
## Anatomy
|
|
13
|
+
|
|
14
|
+
- `Drawer` / `DrawerTrigger` / `DrawerPortal` / `DrawerClose` — vaul plumbing with `data-slot` names (`drawer`, `drawer-trigger`, `drawer-portal`, `drawer-close`).
|
|
15
|
+
- `DrawerOverlay` — scrim in `background-overlay-default`; `data-slot="drawer-overlay"`.
|
|
16
|
+
- `DrawerContent` — the panel; direction-aware chrome via `data-vaul-drawer-direction`; `data-slot="drawer-content"`. Bottom drawers show the grab handle: a 100×8px `rounded-full` bar in `bg-border-default-default`.
|
|
17
|
+
- `DrawerHeader` — `p-4`, centred for top/bottom, left from `md`; `data-slot="drawer-header"`.
|
|
18
|
+
- `DrawerTitle` / `DrawerDescription` — `type-drawer-title` / `-description`.
|
|
19
|
+
- `DrawerFooter` — bottom-pinned (`mt-auto`), stacked full-width children; `data-slot="drawer-footer"`.
|
|
20
|
+
|
|
21
|
+
## API
|
|
22
|
+
|
|
23
|
+
### Drawer (root — vaul props)
|
|
24
|
+
|
|
25
|
+
| Prop | Type | Default | Notes |
|
|
26
|
+
| --- | --- | --- | --- |
|
|
27
|
+
| `direction` | `"bottom" \| "top" \| "left" \| "right"` | `"bottom"` | Sets edge, radius, and handle. |
|
|
28
|
+
| `open` / `defaultOpen` / `onOpenChange` | vaul props | — | Controlled / uncontrolled. |
|
|
29
|
+
| `snapPoints`, `dismissible`, `modal`, … | vaul props | — | Pass through untouched. |
|
|
30
|
+
|
|
31
|
+
### DrawerContent / others
|
|
32
|
+
|
|
33
|
+
Standard vaul/element props plus `className`; no additional props of their own.
|
|
34
|
+
|
|
35
|
+
## Variants & sizes
|
|
36
|
+
|
|
37
|
+
Direction is the axis.
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import {
|
|
41
|
+
Drawer, DrawerTrigger, DrawerContent, DrawerHeader,
|
|
42
|
+
DrawerTitle, DrawerDescription, DrawerFooter, DrawerClose, Button,
|
|
43
|
+
} from "@trading-game/design-intelligence-layer"
|
|
44
|
+
|
|
45
|
+
<Drawer>
|
|
46
|
+
<DrawerTrigger asChild><Button>Set amount</Button></DrawerTrigger>
|
|
47
|
+
<DrawerContent>
|
|
48
|
+
<DrawerHeader>
|
|
49
|
+
<DrawerTitle>Trade amount</DrawerTitle>
|
|
50
|
+
<DrawerDescription>Balance: 1,240.50</DrawerDescription>
|
|
51
|
+
</DrawerHeader>
|
|
52
|
+
<DrawerFooter>
|
|
53
|
+
<Button>Confirm</Button>
|
|
54
|
+
<DrawerClose asChild><Button variant="tertiary">Cancel</Button></DrawerClose>
|
|
55
|
+
</DrawerFooter>
|
|
56
|
+
</DrawerContent>
|
|
57
|
+
</Drawer>
|
|
58
|
+
|
|
59
|
+
<Drawer direction="right">…</Drawer>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
| Direction | Chrome |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| `bottom` | `rounded-t-2xl`, top hairline, `max-h-[80vh]`, grab handle |
|
|
65
|
+
| `top` | `rounded-b-2xl`, bottom hairline, `max-h-[80vh]`, no handle |
|
|
66
|
+
| `left` / `right` | square corners, side hairline, `w-3/4` capped at `sm:max-w-sm` |
|
|
67
|
+
|
|
68
|
+
## Tokens
|
|
69
|
+
|
|
70
|
+
**Colour (semantic):** `background-primary-surface` (panel), `background-overlay-default` (scrim), `border-border-default-default` (edge hairline and the grab handle fill), `text-prominent-default` (title), `text-subtle-default` (description).
|
|
71
|
+
**Type (private):**
|
|
72
|
+
|
|
73
|
+
| Token | Value |
|
|
74
|
+
| --- | --- |
|
|
75
|
+
| `--drawer-title-*` | 18 / semibold (600) / 24 |
|
|
76
|
+
| `--drawer-description-*` | 14 / regular (400) / 20 |
|
|
77
|
+
|
|
78
|
+
## Behaviour
|
|
79
|
+
|
|
80
|
+
- vaul provides the drag/velocity dismissal, snap points, and background scaling; direction is read from `data-vaul-drawer-direction` at render.
|
|
81
|
+
- Only the detached edge is rounded (`rounded-t-2xl` bottom, `rounded-b-2xl` top — the 18px card radius); side drawers stay square because they meet two screen edges.
|
|
82
|
+
- The grab handle renders only on bottom drawers — the direction users can actually drag from comfortably.
|
|
83
|
+
- Top/bottom drawers cap at 80vh and reserve 96px (`mb-24`/`mt-24`) of visible page.
|
|
84
|
+
- `DrawerFooter` forces every child full-width (`[&>*]:w-full`) — the mobile button stack.
|
|
85
|
+
- Header text centres on top/bottom drawers and left-aligns from `md`.
|
|
86
|
+
|
|
87
|
+
## Do / Don't
|
|
88
|
+
|
|
89
|
+
**Do**
|
|
90
|
+
- Default to `direction="bottom"` on mobile; the handle plus swipe is the primary dismissal.
|
|
91
|
+
- Put actions in `DrawerFooter` so they pin to the bottom and go full-width.
|
|
92
|
+
- Keep titles at 18/semibold/24.
|
|
93
|
+
|
|
94
|
+
**Don't**
|
|
95
|
+
- Don't add your own rounding to side drawers — square is deliberate.
|
|
96
|
+
- Don't recolour the grab handle; it is `bg-border-default-default` by contract.
|
|
97
|
+
- Don't exceed 80vh with content — let the body scroll inside the panel instead.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Dropdown Menu
|
|
2
|
+
|
|
3
|
+
Trigger-anchored action menu with items, checkboxes, radios, labels, shortcuts, and nested submenus.
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
- Overflow ("⋯") actions, account menus, per-row operations — a short list of commands behind a visible trigger.
|
|
8
|
+
|
|
9
|
+
**When not:** For right-click actions use [Context Menu](./context-menu.md). For picking a form value use Select. For searchable long lists use [Command](./command.md).
|
|
10
|
+
|
|
11
|
+
## Anatomy
|
|
12
|
+
|
|
13
|
+
- `DropdownMenu` / `DropdownMenuTrigger` / `DropdownMenuPortal` / `DropdownMenuGroup` / `DropdownMenuRadioGroup` / `DropdownMenuSub` — Radix plumbing, each with its `data-slot` (`dropdown-menu`, `dropdown-menu-trigger`, …).
|
|
14
|
+
- `DropdownMenuContent` / `DropdownMenuSubContent` — panel: `rounded-lg` (10px), hairline border, surface fill, `p-1`, `min-w-[8rem]`; scrolls within the Radix available height.
|
|
15
|
+
- `DropdownMenuItem` — action row, `rounded-md` (8px); `data-inset`, `data-variant`.
|
|
16
|
+
- `DropdownMenuCheckboxItem` / `DropdownMenuRadioItem` — `pl-8` rows with left indicators (16px check / 8px filled circle).
|
|
17
|
+
- `DropdownMenuLabel` — heading row; `DropdownMenuSeparator` — 1px hairline; `DropdownMenuShortcut` — right-aligned hint; `DropdownMenuSubTrigger` — row with trailing chevron.
|
|
18
|
+
|
|
19
|
+
## API
|
|
20
|
+
|
|
21
|
+
### DropdownMenuContent
|
|
22
|
+
|
|
23
|
+
| Prop | Type | Default | Notes |
|
|
24
|
+
| --- | --- | --- | --- |
|
|
25
|
+
| `sideOffset` | `number` | `4` | Gap from the trigger. |
|
|
26
|
+
| `align`, `side`, … | Radix props | — | Positioning pass-through. |
|
|
27
|
+
|
|
28
|
+
### DropdownMenuItem
|
|
29
|
+
|
|
30
|
+
| Prop | Type | Default | Notes |
|
|
31
|
+
| --- | --- | --- | --- |
|
|
32
|
+
| `variant` | `"default" \| "destructive"` | `"default"` | Destructive = error ink; focus paints the error tint. |
|
|
33
|
+
| `inset` | `boolean` | — | `pl-8` alignment with indicator rows. |
|
|
34
|
+
| `onSelect`, `disabled` | Radix props | — | Standard item behaviour. |
|
|
35
|
+
|
|
36
|
+
### DropdownMenuCheckboxItem / DropdownMenuRadioItem
|
|
37
|
+
|
|
38
|
+
| Prop | Type | Default | Notes |
|
|
39
|
+
| --- | --- | --- | --- |
|
|
40
|
+
| `checked` | `boolean \| "indeterminate"` | — | Checkbox item. |
|
|
41
|
+
| `value` | `string` | — | Radio item within `DropdownMenuRadioGroup`. |
|
|
42
|
+
|
|
43
|
+
`DropdownMenuSubTrigger` and `DropdownMenuLabel` also take `inset`.
|
|
44
|
+
|
|
45
|
+
## Variants & sizes
|
|
46
|
+
|
|
47
|
+
One panel size; rows vary by kind.
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
import {
|
|
51
|
+
DropdownMenu, DropdownMenuTrigger, DropdownMenuContent,
|
|
52
|
+
DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator,
|
|
53
|
+
DropdownMenuShortcut, Button,
|
|
54
|
+
} from "@trading-game/design-intelligence-layer"
|
|
55
|
+
|
|
56
|
+
<DropdownMenu>
|
|
57
|
+
<DropdownMenuTrigger asChild>
|
|
58
|
+
<Button variant="secondary" size="icon-sm" aria-label="More"><MoreHorizontal /></Button>
|
|
59
|
+
</DropdownMenuTrigger>
|
|
60
|
+
<DropdownMenuContent align="end">
|
|
61
|
+
<DropdownMenuLabel>Position</DropdownMenuLabel>
|
|
62
|
+
<DropdownMenuItem>Edit alert<DropdownMenuShortcut>⌘E</DropdownMenuShortcut></DropdownMenuItem>
|
|
63
|
+
<DropdownMenuSeparator />
|
|
64
|
+
<DropdownMenuItem variant="destructive">Close position</DropdownMenuItem>
|
|
65
|
+
</DropdownMenuContent>
|
|
66
|
+
</DropdownMenu>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Tokens
|
|
70
|
+
|
|
71
|
+
**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).
|
|
72
|
+
**Type (private):** shared `--menu-*` family — item 14/regular/20, heading 12/medium/16, trigger 14/medium/20, shortcut 12/regular/16.
|
|
73
|
+
|
|
74
|
+
## Behaviour
|
|
75
|
+
|
|
76
|
+
- Radix manages open state, positioning, collision, typeahead, and keyboard navigation; the panel portals with fade/zoom/slide-per-side animation.
|
|
77
|
+
- Row focus uses the hover glaze (`focus:bg-background-hover-default`), never a brand-selected fill; an open sub-trigger holds the glaze.
|
|
78
|
+
- Destructive rows force error ink on their icons (`*:[svg]:text-text-error-default!`).
|
|
79
|
+
- Disabled rows: `pointer-events-none opacity-24`.
|
|
80
|
+
- Radius contract: panel `rounded-lg` 10px, rows `rounded-md` 8px.
|
|
81
|
+
|
|
82
|
+
## Do / Don't
|
|
83
|
+
|
|
84
|
+
**Do**
|
|
85
|
+
- Wrap the trigger around a real Button with `asChild`; icon triggers are circles.
|
|
86
|
+
- Use `align="end"` when the trigger sits at a row's right edge.
|
|
87
|
+
- Use `inset` on plain items sharing a menu with checkbox/radio rows.
|
|
88
|
+
|
|
89
|
+
**Don't**
|
|
90
|
+
- Don't use a dropdown menu as a value picker in forms — that is Select.
|
|
91
|
+
- Don't restyle focused rows with brand-selected tokens; menus highlight with the hover glaze.
|
|
92
|
+
- Don't exceed one submenu level or bury the destructive action mid-list — last, after a separator.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Empty
|
|
2
|
+
|
|
3
|
+
Centered empty-state block for pages, panels, and lists that currently have nothing to show.
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
- A list, table, or search result set has no rows and you need to explain why and offer a next step.
|
|
8
|
+
- First-run surfaces where the user has not created anything yet.
|
|
9
|
+
|
|
10
|
+
**When not:** For inline row content inside a populated list use [Item](./item.md). For transient status feedback use a banner or toast, not an empty state.
|
|
11
|
+
|
|
12
|
+
## Anatomy
|
|
13
|
+
|
|
14
|
+
- `Empty` — dashed-border container, `rounded-2xl`, `p-6 md:p-12`, centered column. `data-slot="empty"`.
|
|
15
|
+
- `EmptyHeader` — stacks media, title, description (`max-w-sm`, `gap-2`). `data-slot="empty-header"`.
|
|
16
|
+
- `EmptyMedia` — icon or illustration slot above the text. `data-slot="empty-icon"`.
|
|
17
|
+
- `EmptyTitle` — heading line. `data-slot="empty-title"`.
|
|
18
|
+
- `EmptyDescription` — supporting copy; nested links get an underline and brand hover ink. `data-slot="empty-description"`.
|
|
19
|
+
- `EmptyContent` — action area (buttons, links), `max-w-sm`, `gap-4`. `data-slot="empty-content"`.
|
|
20
|
+
|
|
21
|
+
## API
|
|
22
|
+
|
|
23
|
+
### EmptyMedia
|
|
24
|
+
|
|
25
|
+
| Prop | Type | Default | Notes |
|
|
26
|
+
| --- | --- | --- | --- |
|
|
27
|
+
| `variant` | `"default" \| "icon"` | `"default"` | `icon` renders a 40px (`size-10`) `rounded-lg` tile on `bg-background-secondary-surface` with `text-icon-prominent-default`; inner svg is `size-6`. `default` is transparent, for illustrations. |
|
|
28
|
+
|
|
29
|
+
All other parts accept only standard `div`/`p` props plus `className`.
|
|
30
|
+
|
|
31
|
+
## Variants & sizes
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import {
|
|
35
|
+
Empty,
|
|
36
|
+
EmptyHeader,
|
|
37
|
+
EmptyMedia,
|
|
38
|
+
EmptyTitle,
|
|
39
|
+
EmptyDescription,
|
|
40
|
+
EmptyContent,
|
|
41
|
+
Button,
|
|
42
|
+
} from "@trading-game/design-intelligence-layer"
|
|
43
|
+
import { InboxIcon } from "lucide-react"
|
|
44
|
+
|
|
45
|
+
<Empty>
|
|
46
|
+
<EmptyHeader>
|
|
47
|
+
<EmptyMedia variant="icon">
|
|
48
|
+
<InboxIcon />
|
|
49
|
+
</EmptyMedia>
|
|
50
|
+
<EmptyTitle>No open positions</EmptyTitle>
|
|
51
|
+
<EmptyDescription>Your trades will appear here once you place one.</EmptyDescription>
|
|
52
|
+
</EmptyHeader>
|
|
53
|
+
<EmptyContent>
|
|
54
|
+
<Button>Start trading</Button>
|
|
55
|
+
</EmptyContent>
|
|
56
|
+
</Empty>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Tokens
|
|
60
|
+
|
|
61
|
+
**Colour (semantic):**
|
|
62
|
+
- `border-border-default-default` (dashed container border)
|
|
63
|
+
- `bg-background-secondary-surface` + `text-icon-prominent-default` (icon tile)
|
|
64
|
+
- `text-text-prominent-default` (title)
|
|
65
|
+
- `text-text-subtle-default` (description), links hover to `text-text-brand-default`
|
|
66
|
+
|
|
67
|
+
**Type (private):**
|
|
68
|
+
- `--empty-title-*`: 18 / semibold / 24
|
|
69
|
+
- `--empty-description-*`: 14 / regular / 20 (used by both `EmptyDescription` and `EmptyContent`)
|
|
70
|
+
|
|
71
|
+
## Behaviour
|
|
72
|
+
|
|
73
|
+
- Purely presentational; no state.
|
|
74
|
+
- Container padding steps from `p-6` to `p-12` at the `md` breakpoint.
|
|
75
|
+
- Links inside `EmptyDescription` are underlined at rest (`underline-offset-4`) — the one place this system underlines at rest, because the surrounding copy gives no other affordance.
|
|
76
|
+
|
|
77
|
+
## Do / Don't
|
|
78
|
+
|
|
79
|
+
**Do**
|
|
80
|
+
- Use `EmptyMedia variant="icon"` for glyphs — the 40px secondary-surface tile is the standard treatment.
|
|
81
|
+
- Keep the title at the token size (18/semibold/24); semibold 600 is the system's maximum weight.
|
|
82
|
+
- Put actions in `EmptyContent`, not loose under the header.
|
|
83
|
+
|
|
84
|
+
**Don't**
|
|
85
|
+
- Don't add a shadow or solid fill to the container — the dashed hairline on a transparent ground is the design.
|
|
86
|
+
- Don't exceed semibold on the title; no bold anywhere in components.
|
|
87
|
+
- Don't swap the icon tile's colours to raw primitives; the semantic pair is the contract.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Field
|
|
2
|
+
|
|
3
|
+
Form-row scaffolding: label, control, helper text, and error wiring, with one `size` prop that sizes the whole frame.
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
- Any labelled form control — wrap the control in `Field` and it gets label typography, spacing, description, and error display for free.
|
|
8
|
+
- Grouped choices: wrap `RadioGroup`/checkbox groups in `FieldSet` + `FieldLegend`; use `FieldLabel` as a choice card around a whole `Field`.
|
|
9
|
+
|
|
10
|
+
**When not:** For a bare label with no field frame use [Label](./label.md). For the text control itself see [Input](./input.md), [Input Group](./input-group.md), or [Native Select](./native-select.md).
|
|
11
|
+
|
|
12
|
+
## Anatomy
|
|
13
|
+
|
|
14
|
+
- `FieldSet` — `<fieldset>` wrapper, `gap-6` (`gap-3` when it directly contains a checkbox/radio group). `data-slot="field-set"`.
|
|
15
|
+
- `FieldLegend` — `<legend>`; `variant="legend"` (16/medium/24) or `"label"` (14/medium/20). `data-slot="field-legend"`.
|
|
16
|
+
- `FieldGroup` — vertical stack of Fields, `gap-7`, container-query scope for responsive orientation. `data-slot="field-group"`.
|
|
17
|
+
- `Field` — the row itself; provides `FieldSizeContext`. `role="group"`, `data-slot="field"`, `data-orientation`, `data-size`.
|
|
18
|
+
- `FieldContent` — flex column for label + description beside a checkbox/radio. `data-slot="field-content"`.
|
|
19
|
+
- `FieldLabel` — a `Label` sized by the field context; becomes a bordered choice card when it wraps a nested `Field`. `data-slot="field-label"`.
|
|
20
|
+
- `FieldTitle` — non-`<label>` title with the same ladder. `data-slot="field-label"`.
|
|
21
|
+
- `FieldDescription` — helper copy, fixed 12/16. `data-slot="field-description"`.
|
|
22
|
+
- `FieldSeparator` — hairline with optional centred text chip. `data-slot="field-separator"`.
|
|
23
|
+
- `FieldError` — `role="alert"` error text, fixed 12/16. `data-slot="field-error"`.
|
|
24
|
+
- `useFieldSize` — hook controls call to read the cascade.
|
|
25
|
+
|
|
26
|
+
## API
|
|
27
|
+
|
|
28
|
+
### Field
|
|
29
|
+
|
|
30
|
+
| Prop | Type | Default | Notes |
|
|
31
|
+
| --- | --- | --- | --- |
|
|
32
|
+
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | Cascades via `FieldSizeContext` to Input, Input Group, and Native Select inside; an explicit `size` on the control wins. Also picks the label ladder and row gap (sm `gap-2`, md/lg `gap-3`). |
|
|
33
|
+
| `orientation` | `"vertical" \| "horizontal" \| "responsive"` | `"vertical"` | `responsive` switches to horizontal at the `@md` width of the enclosing `FieldGroup` container. |
|
|
34
|
+
|
|
35
|
+
### FieldLegend
|
|
36
|
+
|
|
37
|
+
| Prop | Type | Default | Notes |
|
|
38
|
+
| --- | --- | --- | --- |
|
|
39
|
+
| `variant` | `"legend" \| "label"` | `"legend"` | Chooses `type-field-legend` (16/medium/24) vs `type-field-legend-label` (14/medium/20). The class is picked in JSX because `.type-*` classes are plain CSS — Tailwind `data-[]` variants cannot gate them. |
|
|
40
|
+
|
|
41
|
+
### FieldError
|
|
42
|
+
|
|
43
|
+
| Prop | Type | Default | Notes |
|
|
44
|
+
| --- | --- | --- | --- |
|
|
45
|
+
| `errors` | `Array<{ message?: string } \| undefined>` | — | Deduplicated by `message`; one error renders inline, several render as a `list-disc` `<ul>`. `children` overrides. Renders nothing when both are empty. |
|
|
46
|
+
|
|
47
|
+
### ItemGroup-style parts
|
|
48
|
+
|
|
49
|
+
`FieldSet`, `FieldGroup`, `FieldContent`, `FieldTitle`, `FieldDescription`, `FieldSeparator` take only standard element props plus `className`; `FieldSeparator` additionally accepts `children` for the text chip.
|
|
50
|
+
|
|
51
|
+
## Variants & sizes
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import {
|
|
55
|
+
Field,
|
|
56
|
+
FieldLabel,
|
|
57
|
+
FieldDescription,
|
|
58
|
+
FieldError,
|
|
59
|
+
Input,
|
|
60
|
+
} from "@trading-game/design-intelligence-layer"
|
|
61
|
+
|
|
62
|
+
// One prop sizes label + control together: sm = 32px input + 12/16 label.
|
|
63
|
+
<Field size="sm" data-invalid={hasError || undefined}>
|
|
64
|
+
<FieldLabel htmlFor="stake">Stake</FieldLabel>
|
|
65
|
+
<Input id="stake" inputMode="decimal" aria-invalid={hasError} />
|
|
66
|
+
<FieldDescription>Minimum stake is 0.35 USD.</FieldDescription>
|
|
67
|
+
<FieldError errors={[error]} />
|
|
68
|
+
</Field>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
// Choice card: FieldLabel wrapping a Field grows a border and lights up
|
|
73
|
+
// with the brand-selected tokens when the control inside is checked.
|
|
74
|
+
<FieldLabel>
|
|
75
|
+
<Field orientation="horizontal">
|
|
76
|
+
<RadioGroupItem value="rise" id="rise" />
|
|
77
|
+
<FieldContent>
|
|
78
|
+
<FieldTitle>Rise</FieldTitle>
|
|
79
|
+
<FieldDescription>Win if the exit spot is above the entry spot.</FieldDescription>
|
|
80
|
+
</FieldContent>
|
|
81
|
+
</Field>
|
|
82
|
+
</FieldLabel>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Tokens
|
|
86
|
+
|
|
87
|
+
**Colour (semantic):**
|
|
88
|
+
- `text-text-error-default` — painted on the whole Field via `data-[invalid=true]`; label and error inherit it
|
|
89
|
+
- `text-text-prominent-default` (legend), `text-text-subtle-default` (description, separator chip)
|
|
90
|
+
- Choice card: `border-border-default-default` at rest; checked = `border-border-brand-selected` + `bg-background-brand-selected`
|
|
91
|
+
- Separator chip sits on `bg-background-primary-surface`
|
|
92
|
+
|
|
93
|
+
**Type (private):**
|
|
94
|
+
- `--field-label-*` per size: sm 12/16, md 14/20, lg 16/24 — all medium (`--field-label-font-weight`)
|
|
95
|
+
- `--field-description-*`: 12 / regular / 16 — fixed at every field size (description, error, and separator chip all use it)
|
|
96
|
+
- `--field-legend-*`: 16 / medium / 24; `--field-legend-label-*`: 14 / medium / 20
|
|
97
|
+
|
|
98
|
+
## Behaviour
|
|
99
|
+
|
|
100
|
+
- `size` cascades through `FieldSizeContext`; `Input`, `InputGroup`, and `NativeSelect` default to it, and an explicit size on the control always wins.
|
|
101
|
+
- `data-invalid` on `Field` sets `text-text-error-default` on the group; `Label` deliberately inherits colour, so the label turns red with no extra wiring.
|
|
102
|
+
- Helper text never scales: description/error/separator stay 12/16 at sm, md, and lg.
|
|
103
|
+
- `FieldLabel` wrapping a nested `Field` becomes a `rounded-md` bordered card (`p-4`); `has-data-[state=checked]` switches it to the brand-selected border + fill.
|
|
104
|
+
- Disabled state (`data-disabled=true` on Field) dims labels to 50% opacity.
|
|
105
|
+
- `FieldError` dedupes repeated messages and returns `null` with no content.
|
|
106
|
+
|
|
107
|
+
## Do / Don't
|
|
108
|
+
|
|
109
|
+
**Do**
|
|
110
|
+
- Set size once on `Field` — `<Field size="sm">` gives you the 32px control and the 12/16 label together.
|
|
111
|
+
- Pass `aria-invalid` to the control and `data-invalid` to the Field so ink and border agree.
|
|
112
|
+
- Use `FieldLegend variant="label"` when a radio group sits among plain fields and the 16px legend would shout.
|
|
113
|
+
|
|
114
|
+
**Don't**
|
|
115
|
+
- Don't force an ink on the label — colour inheritance is how the error state reaches it.
|
|
116
|
+
- Don't gate `.type-*` classes with Tailwind variants; they are plain CSS and lose to nothing — pick the class in JSX as `FieldLegend` does.
|
|
117
|
+
- Don't restyle the checked choice card: `bg-background-brand-selected` / `border-border-brand-selected` are the selection contract (light: 10% brand tint + blue-600 ink; dark: solid #121A55 + blue-200/blue-300).
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Hover Card
|
|
2
|
+
|
|
3
|
+
Pointer-hover preview card — rich content that appears when hovering a trigger, for sighted mouse users.
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
- Previewing an entity behind a link or avatar (a trader profile, an instrument summary) without navigating.
|
|
8
|
+
|
|
9
|
+
**When not:** For click-opened content use [Popover](./popover.md). For a short text hint use Tooltip. Hover cards do not open on touch or keyboard — never put essential actions or information in one.
|
|
10
|
+
|
|
11
|
+
## Anatomy
|
|
12
|
+
|
|
13
|
+
- `HoverCard` — Radix root, state owner. `data-slot="hover-card"`.
|
|
14
|
+
- `HoverCardTrigger` — the hoverable element. `data-slot="hover-card-trigger"`.
|
|
15
|
+
- `HoverCardContent` — portalled card, 256px (`w-64`), `rounded-2xl`, `p-4`. `data-slot="hover-card-content"` (portal: `data-slot="hover-card-portal"`).
|
|
16
|
+
|
|
17
|
+
## API
|
|
18
|
+
|
|
19
|
+
### HoverCard (root)
|
|
20
|
+
|
|
21
|
+
Standard Radix `HoverCard.Root` props: `open`, `defaultOpen`, `onOpenChange`, `openDelay`, `closeDelay`.
|
|
22
|
+
|
|
23
|
+
### HoverCardContent
|
|
24
|
+
|
|
25
|
+
| Prop | Type | Default | Notes |
|
|
26
|
+
| --- | --- | --- | --- |
|
|
27
|
+
| `align` | `"start" \| "center" \| "end"` | `"center"` | Radix alignment. |
|
|
28
|
+
| `sideOffset` | `number` | `4` | Gap to the trigger in px. |
|
|
29
|
+
|
|
30
|
+
All other Radix `HoverCard.Content` props pass through.
|
|
31
|
+
|
|
32
|
+
## Variants & sizes
|
|
33
|
+
|
|
34
|
+
No variants. One card shell at `w-64`; override width via `className` when the preview needs more room.
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import {
|
|
38
|
+
HoverCard,
|
|
39
|
+
HoverCardTrigger,
|
|
40
|
+
HoverCardContent,
|
|
41
|
+
} from "@trading-game/design-intelligence-layer"
|
|
42
|
+
|
|
43
|
+
<HoverCard>
|
|
44
|
+
<HoverCardTrigger asChild>
|
|
45
|
+
<a href="/traders/aisha">@aisha</a>
|
|
46
|
+
</HoverCardTrigger>
|
|
47
|
+
<HoverCardContent>
|
|
48
|
+
{/* compose your own content; the shell sets surface, border, radius */}
|
|
49
|
+
</HoverCardContent>
|
|
50
|
+
</HoverCard>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Tokens
|
|
54
|
+
|
|
55
|
+
**Colour (semantic):**
|
|
56
|
+
- `bg-background-primary-surface` (solid card surface)
|
|
57
|
+
- `border-border-default-default` (hairline)
|
|
58
|
+
- `text-text-prominent-default` (default ink)
|
|
59
|
+
|
|
60
|
+
**Type (private):** none — the shell sets no type; compose content with the system's `.type-*` classes or sibling parts (e.g. Popover's title/description pattern).
|
|
61
|
+
|
|
62
|
+
## Behaviour
|
|
63
|
+
|
|
64
|
+
- Opens on pointer hover after Radix's `openDelay`; closes on hover-out. No touch or keyboard path — treat the content as strictly supplementary.
|
|
65
|
+
- Content is portalled and animated (fade + 95% zoom, side-aware slide).
|
|
66
|
+
- Card shell is `rounded-2xl` (18px) with a hairline border and no shadow — surfaces are solid in this system.
|
|
67
|
+
|
|
68
|
+
## Do / Don't
|
|
69
|
+
|
|
70
|
+
**Do**
|
|
71
|
+
- Keep it to previews the user can live without; the same information must be reachable by click or navigation.
|
|
72
|
+
- Keep the shell defaults: solid primary surface, hairline border, 18px radius, 16px padding.
|
|
73
|
+
|
|
74
|
+
**Don't**
|
|
75
|
+
- Don't put buttons or form controls inside — they are unreachable for keyboard and touch users.
|
|
76
|
+
- Don't add a shadow or translucent backdrop; the system's overlay depth cue is the hairline on a solid surface.
|
|
77
|
+
- Don't shrink the radius; overlay cards are `rounded-2xl` by law (menus are the 10px tier, not cards).
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Input Group
|
|
2
|
+
|
|
3
|
+
Input frame with addons — prefixes, suffixes, kbd hints, and inline buttons share one border and one focus ring.
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
- A text control that needs a currency prefix, unit suffix, leading icon, keyboard hint, or an inline action button.
|
|
8
|
+
- Block addons above/below a textarea (e.g. a toolbar row).
|
|
9
|
+
|
|
10
|
+
**When not:** For a plain control use [Input](./input.md). For code entry use [Input OTP](./input-otp.md).
|
|
11
|
+
|
|
12
|
+
## Anatomy
|
|
13
|
+
|
|
14
|
+
- `InputGroup` — the frame; owns border, background, size, variant, and the focus ring. `role="group"`, `data-slot="input-group"`, `data-variant`, `data-size`.
|
|
15
|
+
- `InputGroupInput` — an `Input` stripped of its own frame (`data-slot="input-group-control"`); inherits the group's size/variant via context.
|
|
16
|
+
- `InputGroupTextarea` — same idea for `Textarea`. `data-slot="input-group-control"`.
|
|
17
|
+
- `InputGroupAddon` — addon container; clicking it focuses the input. `data-slot="input-group-addon"`, `data-align`.
|
|
18
|
+
- `InputGroupButton` — a real `Button` compressed to fit the frame.
|
|
19
|
+
- `InputGroupText` — muted inline text/icon span.
|
|
20
|
+
|
|
21
|
+
## API
|
|
22
|
+
|
|
23
|
+
### InputGroup
|
|
24
|
+
|
|
25
|
+
| Prop | Type | Default | Notes |
|
|
26
|
+
| --- | --- | --- | --- |
|
|
27
|
+
| `variant` | `"default" \| "on-brand" \| "inverse"` | `"default"` | Mirrors Input: `on-brand` = `--primitive-black-alpha-32` fill + `--primitive-white-alpha-24` border. `inverse` @deprecated — alias of `on-brand`, removed at v1.0. |
|
|
28
|
+
| `size` | `"sm" \| "md" \| "lg"` | `"md"` (or the enclosing Field's size) | Frame heights `h-8`/`h-10`/`h-12` (32/40/48). |
|
|
29
|
+
|
|
30
|
+
### InputGroupAddon
|
|
31
|
+
|
|
32
|
+
| Prop | Type | Default | Notes |
|
|
33
|
+
| --- | --- | --- | --- |
|
|
34
|
+
| `align` | `"inline-start" \| "inline-end" \| "block-start" \| "block-end"` | `"inline-start"` | Block aligns stack the group vertically (for textareas/toolbars). The input's padding shrinks to `pl-2`/`pr-2` only on the side an addon touches. |
|
|
35
|
+
|
|
36
|
+
### InputGroupButton
|
|
37
|
+
|
|
38
|
+
| Prop | Type | Default | Notes |
|
|
39
|
+
| --- | --- | --- | --- |
|
|
40
|
+
| `size` | `"xs" \| "sm" \| "icon-xs" \| "icon-sm"` | `"xs"` | xs = 24px pill, sm = 32px pill; icon variants are 24/32px circles. Always renders the underlying Button at `size="sm"` type (14/semibold) with height compressed. |
|
|
41
|
+
| `variant` | Button variants | `"secondary"` | Passed straight to `Button`. |
|
|
42
|
+
| `type` | button types | `"button"` | Prevents accidental form submit. |
|
|
43
|
+
|
|
44
|
+
### InputGroupInput
|
|
45
|
+
|
|
46
|
+
| Prop | Type | Default | Notes |
|
|
47
|
+
| --- | --- | --- | --- |
|
|
48
|
+
| `size` / `variant` | as InputGroup | group's values | Context-fed; explicit props win. |
|
|
49
|
+
|
|
50
|
+
## Variants & sizes
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import {
|
|
54
|
+
InputGroup,
|
|
55
|
+
InputGroupAddon,
|
|
56
|
+
InputGroupInput,
|
|
57
|
+
InputGroupText,
|
|
58
|
+
InputGroupButton,
|
|
59
|
+
} from "@trading-game/design-intelligence-layer"
|
|
60
|
+
|
|
61
|
+
// Currency prefix + max button, on the 40px rail.
|
|
62
|
+
<InputGroup>
|
|
63
|
+
<InputGroupAddon>
|
|
64
|
+
<InputGroupText>USD</InputGroupText>
|
|
65
|
+
</InputGroupAddon>
|
|
66
|
+
<InputGroupInput inputMode="decimal" placeholder="0.00" />
|
|
67
|
+
<InputGroupAddon align="inline-end">
|
|
68
|
+
<InputGroupButton>Max</InputGroupButton>
|
|
69
|
+
</InputGroupAddon>
|
|
70
|
+
</InputGroup>
|
|
71
|
+
|
|
72
|
+
// Brand surface.
|
|
73
|
+
<InputGroup variant="on-brand" size="lg"> … </InputGroup>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Tokens
|
|
77
|
+
|
|
78
|
+
**Colour (semantic):**
|
|
79
|
+
- default: `border-border-default-default`, `bg-background-primary-surface`; focus (via `:has` on the control) `border-ring-focus-default` + `ring-ring-focus-soft`
|
|
80
|
+
- error: `border-border-error-default` + `ring-ring-error-soft` when any slot is `aria-invalid`
|
|
81
|
+
- addon/text ink: `text-text-subtle-default`; inside on-brand groups it swaps to `--primitive-white-alpha-64`
|
|
82
|
+
- on-brand (alpha primitives): fill `--primitive-black-alpha-32`, border `--primitive-white-alpha-24`, focus border `background-static-white` + ring `--primitive-white-alpha-40`
|
|
83
|
+
|
|
84
|
+
**Type (private):** the group inherits the `--input-*` ladder via `data-size` CSS — sm 12/16, md 14/20, lg 16/24 regular; addons and `InputGroupText` ride the same ladder. Buttons keep Button's sm type (14/semibold).
|
|
85
|
+
|
|
86
|
+
## Behaviour
|
|
87
|
+
|
|
88
|
+
- The frame carries the border, background, focus ring, and error ring; the inner control is transparent, borderless, ring-less (`rounded-none border-0 bg-transparent focus-visible:ring-0`).
|
|
89
|
+
- Size and variant flow to `InputGroupInput` through `InputGroupSizeContext`/`InputGroupVariantContext`; inside a [Field](./field.md), the Field's size supplies the group default. Explicit props win at every level.
|
|
90
|
+
- Clicking anywhere on an addon focuses the input, unless the click landed on a button.
|
|
91
|
+
- Block addons (`block-start`/`block-end`) switch the group to `h-auto` column layout.
|
|
92
|
+
- Icon sizes track the rail: sm addons get `size-3.5` (14px) icons, md/lg `size-4` (16px).
|
|
93
|
+
- `inverse` is a deprecated render-identical alias of `on-brand`; deleted at v1.0.
|
|
94
|
+
|
|
95
|
+
## Do / Don't
|
|
96
|
+
|
|
97
|
+
**Do**
|
|
98
|
+
- Use `InputGroupInput`, never a raw `Input`, inside the frame — otherwise you get two borders and two rings.
|
|
99
|
+
- Use `InputGroupButton` for inline actions: real Button semantics at xs 24px / sm 32px, pills and circles only.
|
|
100
|
+
- Put `aria-invalid` on the control; the frame paints the error border and 20% ring for you.
|
|
101
|
+
|
|
102
|
+
**Don't**
|
|
103
|
+
- Don't add padding to the input to make room for addons — the frame already trims to `pl-2`/`pr-2` on the addon side and keeps 12px on the free side.
|
|
104
|
+
- Don't restyle addon ink on brand surfaces by hand; the group's `data-variant` does the white-alpha-64 swap.
|
|
105
|
+
- Don't use `variant="inverse"` in new code.
|