@dreamtree-org/twreact-ui 2.0.9 → 2.1.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/DESIGN.md +3 -0
- package/README.md +9 -4
- package/ai-skills/dreamtree-ui.md +12 -8
- package/dist/ai-schema.json +314 -70
- package/dist/components/blocks/AppShell.d.ts.map +1 -1
- package/dist/components/blocks/ProductCard.d.ts +4 -0
- package/dist/components/blocks/ProductCard.d.ts.map +1 -0
- package/dist/components/blocks/ProductCardParts.d.ts +19 -0
- package/dist/components/blocks/ProductCardParts.d.ts.map +1 -0
- package/dist/components/blocks/StatCard.d.ts.map +1 -1
- package/dist/components/core/Button.d.ts.map +1 -1
- package/dist/components/core/OptionCard.d.ts +14 -0
- package/dist/components/core/OptionCard.d.ts.map +1 -0
- package/dist/components/core/OptionCardGroup.d.ts +2 -0
- package/dist/components/core/OptionCardGroup.d.ts.map +1 -0
- package/dist/components/core/QuantityStepper.d.ts +8 -0
- package/dist/components/core/QuantityStepper.d.ts.map +1 -0
- package/dist/components/utility/ImageGallery.d.ts +4 -0
- package/dist/components/utility/ImageGallery.d.ts.map +1 -0
- package/dist/components/utility/imageGalleryParts.d.ts +55 -0
- package/dist/components/utility/imageGalleryParts.d.ts.map +1 -0
- package/dist/esm/components/blocks/AppShell.js +17 -6
- package/dist/esm/components/blocks/ProductCard.js +122 -0
- package/dist/esm/components/blocks/ProductCardParts.js +128 -0
- package/dist/esm/components/blocks/StatCard.js +20 -5
- package/dist/esm/components/core/Button.js +17 -2
- package/dist/esm/components/core/OptionCard.js +340 -0
- package/dist/esm/components/core/QuantityStepper.js +194 -0
- package/dist/esm/components/utility/ImageGallery.js +150 -0
- package/dist/esm/components/utility/imageGalleryParts.js +380 -0
- package/dist/esm/index.js +4 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.esm.css +1 -1
- package/dist/index.esm.js +1843 -502
- package/dist/index.js +1847 -501
- package/dist/tokens/tailwind3-preset.cjs +4 -1
- package/dist/tokens/theme.css +3 -0
- package/dist/tokens/tokens.json +7 -0
- package/dist/tokens/vars.css +2 -0
- package/dist/types/blocks.d.ts +61 -1
- package/dist/types/blocks.d.ts.map +1 -1
- package/dist/types/index.d.ts +102 -1
- package/dist/types/index.d.ts.map +1 -1
- package/doc/README.md +5 -1
- package/mcp/catalog.snapshot.json +479 -8
- package/package.json +1 -1
- package/tokens/brand.schema.json +22 -0
- package/tokens/lib.cjs +26 -0
- package/tokens/tokens.json +2 -1
package/DESIGN.md
CHANGED
|
@@ -11,6 +11,7 @@ colors:
|
|
|
11
11
|
shell: "#0f172a"
|
|
12
12
|
shell-foreground: "#cbd5e1"
|
|
13
13
|
shell-active: "#1e293b"
|
|
14
|
+
primary-foreground: "#ffffff"
|
|
14
15
|
primary: "#2563eb"
|
|
15
16
|
secondary: "#475569"
|
|
16
17
|
success: "#16a34a"
|
|
@@ -26,6 +27,7 @@ colorsDark:
|
|
|
26
27
|
shell: "#020617"
|
|
27
28
|
shell-foreground: "#cbd5e1"
|
|
28
29
|
shell-active: "#1e293b"
|
|
30
|
+
primary-foreground: "#ffffff"
|
|
29
31
|
typography:
|
|
30
32
|
fontFamily: "Inter"
|
|
31
33
|
pageTitle: { size: 24px, lineHeight: 32px, weight: 600 }
|
|
@@ -77,6 +79,7 @@ Every Dreamtree product screen, and every prototype generated by Dreamtree Studi
|
|
|
77
79
|
| `shell` | `bg-shell` / `text-shell` | #0f172a | #020617 | App shell sidebar background. |
|
|
78
80
|
| `shell-foreground` | `bg-shell-foreground` / `text-shell-foreground` | #cbd5e1 | #cbd5e1 | Sidebar text. |
|
|
79
81
|
| `shell-active` | `bg-shell-active` / `text-shell-active` | #1e293b | #1e293b | Active sidebar item fill. |
|
|
82
|
+
| `primary-foreground` | `bg-primary-foreground` / `text-primary-foreground` | #ffffff | #ffffff | Text, icons, tiles (primary-foreground/12) and focus rings on a primary-600 to primary-900 fill, such as a brand band. A brand that changes color.primary gets white or role.foreground, whichever contrasts more with its primary-700. |
|
|
80
83
|
|
|
81
84
|
- **One accent per screen.** `primary` marks the primary action, the active nav item, the current step and progress. Red (`error`) is only for errors, overdue items and destructive actions.
|
|
82
85
|
- Badge and status fills use the 100 step with 800 text, which keeps contrast at or above 4.5:1.
|
package/README.md
CHANGED
|
@@ -227,7 +227,8 @@ library to refresh it.
|
|
|
227
227
|
Versatile button component with multiple variants and states.
|
|
228
228
|
|
|
229
229
|
**Props:**
|
|
230
|
-
- `variant`: 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'success' | 'warning' (default: 'primary')
|
|
230
|
+
- `variant`: 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'success' | 'warning' | 'link' (default: 'primary'). `link` is a text link (no background or padding, underline on hover, 44px on touch); colour it with `tone`
|
|
231
|
+
- `tone`: 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'info' — text colour of `variant="link"` (default: 'primary')
|
|
231
232
|
- `size`: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' (default: 'md') — heights h-7 / h-8 / h-10 / h-12 / h-14 / h-16
|
|
232
233
|
- `disabled`: boolean
|
|
233
234
|
- `loading`: boolean
|
|
@@ -767,6 +768,9 @@ Interaction primitives that products used to compose by hand. All are `forwardRe
|
|
|
767
768
|
| [`SegmentedControl`](doc/SegmentedControl.md) | Core | Radiogroup view toggle (list/board, day/week): `options`, `value`/`defaultValue` + `onChange`, `size`, `fullWidth`, `name` for forms; arrow keys move the selection. Use `Tabs variant="segmented"` when the choice switches panels |
|
|
768
769
|
| [`SwipeableListItem`](doc/SwipeableListItem.md) | Core | Mail-style swipe-to-reveal row: `leadingActions`/`trailingActions`, `fullSwipe` commits the outer action, controlled `open`; a "More actions" button reaches every action without gestures. Table cards take `swipeActions` |
|
|
769
770
|
| [`SearchField`](doc/SearchField.md) | Core | iOS-style search input: clear button, phone Cancel, `onValueChange`/`onSearch`; `results`/`renderResults` show in a Sheet on phones and a popover elsewhere (`presentation`) |
|
|
771
|
+
| [`QuantityStepper`](doc/QuantityStepper.md) | Core | − / value / + spinbutton for cart lines and buy boxes: `value`/`defaultValue` + `onChange(number)`, `min`/`max`/`step`, `size`, `loading`, `label`/`hideLabel`, `formatValue`; arrows/PageUp/PageDown/Home/End, 44px buttons on touch |
|
|
772
|
+
| [`OptionCard`](doc/OptionCard.md) / `OptionCardGroup` | Core | Selectable choice tiles (`title`, `description`, `media`, `badge`) for template, block and starter-kit pickers. The group (`value`/`onChange`, `columns` 1–4, `multiple`) is a radiogroup (checkbox group with `multiple`) with arrow-key focus; a standalone card is a toggle (`aria-pressed`) |
|
|
773
|
+
| [`ImageGallery`](doc/ImageGallery.md) | Utility | Product photos: `layout` `thumbs-below` / `thumbs-side` / `mosaic`, `ratio`, controlled `index` / `onIndexChange`, swipe, arrow keys, click-to-`zoom`, a Dialog `lightbox`, counter badge, `placeholder` for missing images |
|
|
770
774
|
| [`PullToRefresh`](doc/PullToRefresh.md) | Utility | Touch pull-to-refresh around a scroller: `onRefresh` returns a promise, `threshold`, `maxPull`, `disabled`, `scrollRef` |
|
|
771
775
|
| [`BottomActionBar`](doc/BottomActionBar.md) | Block | Sticky bottom bar for a form's primary action: `pb-safe`, rides above the on-screen keyboard (`keyboardAware`), `stackOnMobile`; add `<BottomActionBar.Spacer />` at the end of the page |
|
|
772
776
|
|
|
@@ -859,9 +863,10 @@ Page-level patterns extracted from the Dreamtree products. Build pages from thes
|
|
|
859
863
|
|
|
860
864
|
| Block | Use for |
|
|
861
865
|
|---|---|
|
|
862
|
-
| [`AppShell`](doc/AppShell.md) | Product shell: sidebar, top bar, mobile drawer (`nav`, `linkAs`) |
|
|
866
|
+
| [`AppShell`](doc/AppShell.md) | Product shell: sidebar, top bar, mobile drawer (`nav`, `linkAs`); control the drawer with `navOpen` / `onNavOpenChange`, drop the desktop top bar with `topbarFrom="below-lg"` |
|
|
863
867
|
| [`PageHeader`](doc/PageHeader.md) | The page's `h1`, subtitle, one primary action, back link |
|
|
864
|
-
| [`StatCard`](doc/StatCard.md) | KPI tile with icon, tone and change |
|
|
868
|
+
| [`StatCard`](doc/StatCard.md) | KPI tile with icon, tone and change; `children` render under the value (a `ProgressBar`, a link), `tinted` colours the whole card by `tone` |
|
|
869
|
+
| [`ProductCard`](doc/ProductCard.md) | Storefront product tile: cover (second image or zoom on hover), badges, price with struck-through `compareAtPrice` and auto discount, one stretched link (`href`/`to`/`as`), `actions`, compare toggle (`selectable`) |
|
|
865
870
|
| [`StatusPill`](doc/StatusPill.md) | Domain status → tone and human label (never a raw enum) |
|
|
866
871
|
| [`EmptyState`](doc/EmptyState.md) / [`ErrorState`](doc/ErrorState.md) / [`LoadingState`](doc/LoadingState.md) | The three states every data card needs |
|
|
867
872
|
| [`ConfirmDialog`](doc/ConfirmDialog.md) | Confirmation step — replaces `window.confirm()` |
|
|
@@ -944,7 +949,7 @@ module.exports = {
|
|
|
944
949
|
@source "../node_modules/@dreamtree-org/twreact-ui/dist/index.esm.js";
|
|
945
950
|
```
|
|
946
951
|
|
|
947
|
-
The palettes are `primary`, `secondary`, `success`, `warning`, `error`, `info` and `gray` (steps 50–950), so `bg-info-50` / `text-info-600` work out of the box; `info` is for informational and scheduled states that are not the brand accent. This adds semantic utilities next to the palettes: `bg-surface`, `bg-background`, `text-foreground`, `text-muted`, `border-line`, `bg-shell`, `text-micro`, `rounded-control` and `rounded-card`. Non-colour scales come from the same file: type roles (`text-page-title`, `text-section-title`, `text-body`, `text-caption`, `text-kpi`), `rounded-sheet` / `rounded-pill`, `font-mono`, elevation (`shadow-xs` … `shadow-xl`, `shadow-overlay`; deeper in dark mode via `--dt-shadow-strength`), z-layers (`z-sticky`, `z-dropdown`, `z-overlay`, `z-modal`, `z-popover`, `z-toast`, `z-tooltip`) and motion (`duration-fast|base|slow`, `ease-standard|emphasized|spring-out`). The Tailwind 3 preset and Tailwind 4 theme include them. Add a brand by creating `tokens/brands/<name>.json`; it overrides only the steps and roles you list.
|
|
952
|
+
The palettes are `primary`, `secondary`, `success`, `warning`, `error`, `info` and `gray` (steps 50–950), so `bg-info-50` / `text-info-600` work out of the box; `info` is for informational and scheduled states that are not the brand accent. This adds semantic utilities next to the palettes: `bg-surface`, `bg-background`, `text-foreground`, `text-muted`, `border-line`, `bg-shell`, `text-primary-foreground` (text, `bg-primary-foreground/12` tiles and `ring-primary-foreground` on a `bg-primary-600`–`900` band; a brand with a light primary gets dark text), `text-micro`, `rounded-control` and `rounded-card`. Non-colour scales come from the same file: type roles (`text-page-title`, `text-section-title`, `text-body`, `text-caption`, `text-kpi`), `rounded-sheet` / `rounded-pill`, `font-mono`, elevation (`shadow-xs` … `shadow-xl`, `shadow-overlay`; deeper in dark mode via `--dt-shadow-strength`), z-layers (`z-sticky`, `z-dropdown`, `z-overlay`, `z-modal`, `z-popover`, `z-toast`, `z-tooltip`) and motion (`duration-fast|base|slow`, `ease-standard|emphasized|spring-out`). The Tailwind 3 preset and Tailwind 4 theme include them. Add a brand by creating `tokens/brands/<name>.json`; it overrides only the steps and roles you list.
|
|
948
953
|
|
|
949
954
|
**Design rules:** [`DESIGN.md`](DESIGN.md) is the rule set for product screens and AI agents: tokens only, blocks first, loading, empty and error states, 390 px, no native dialogs. It is generated from the tokens (`npm run build:tokens`).
|
|
950
955
|
|
|
@@ -128,11 +128,11 @@ The library exports four kinds of things from `@dreamtree-org/twreact-ui`:
|
|
|
128
128
|
|
|
129
129
|
| Group | Components |
|
|
130
130
|
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
131
|
-
| **Core** | `Input`, `Button`, `Select`, `Table`, `Form`, `Accordion`, `Checkbox`, `ColorPicker`, `DatePicker`, `DateRangePicker`, `Loader`, `LocationPicker`, `PriceRangePicker`, `ProgressBar`, `Radio`, `Rate`, `RoundedTag`, `Skeleton`, `Switch`, `Tabs`, `ThreeDotPopover`, `Tooltip`, `SpeechToText`, `TextToSpeech`, `Textarea`, `Slider`, `Popover`, `SegmentedControl`, `SwipeableListItem` (swipe-to-reveal row actions), `SearchField` |
|
|
131
|
+
| **Core** | `Input`, `Button`, `Select`, `Table`, `Form`, `Accordion`, `Checkbox`, `ColorPicker`, `DatePicker`, `DateRangePicker`, `Loader`, `LocationPicker`, `PriceRangePicker`, `ProgressBar`, `Radio`, `Rate`, `RoundedTag`, `Skeleton`, `Switch`, `Tabs`, `ThreeDotPopover`, `Tooltip`, `SpeechToText`, `TextToSpeech`, `Textarea`, `Slider`, `Popover`, `SegmentedControl`, `SwipeableListItem` (swipe-to-reveal row actions), `SearchField`, `QuantityStepper` (− / value / + spinbutton), `OptionCard` + `OptionCardGroup` (selectable choice tiles) |
|
|
132
132
|
| **Navigation** | `Sidebar`, `Navbar`, `FootNav`, `Breadcrumbs` |
|
|
133
133
|
| **Feedback** | `Dialog`, `Toast` (+ `ToastProvider`, `ToastContainer`, `useToast`), `Alert`, `Drawer` (side sheet; `isOpen`/`onClose` like Dialog), `Sheet` (+ `Sheet.Header/Body/Footer`; phone sheet with snap points), `ActionSheet` (iOS action sheet: `actions` + separate Cancel), `Tour` (onboarding spotlight) |
|
|
134
|
-
| **Utility** | `Badge`, `Avatar`, `AvatarGroup` (overlapping stack + `max` "+N"), `Card`, `Pagination`, `Stepper`, `FileUpload`, `Condition`, `Carousel`, `Sortable` (drag + keyboard reorder), `SortableGroup` (move items between `Sortable` lists, `canDrop`), `PullToRefresh` (touch pull-to-refresh around a scroller) |
|
|
135
|
-
| **Blocks** | `AppShell`, `PageHeader`, `StatCard`, `StatusPill`, `EmptyState`, `ErrorState`, `LoadingState`, `ConfirmDialog`, `BottomActionBar` (+ `BottomActionBar.Spacer`) — page-level patterns; build pages from these before composing layouts by hand |
|
|
134
|
+
| **Utility** | `Badge`, `Avatar`, `AvatarGroup` (overlapping stack + `max` "+N"), `Card`, `Pagination`, `Stepper`, `FileUpload`, `Condition`, `Carousel`, `Sortable` (drag + keyboard reorder), `SortableGroup` (move items between `Sortable` lists, `canDrop`), `PullToRefresh` (touch pull-to-refresh around a scroller), `ImageGallery` (product photos: thumbnails / mosaic, swipe, zoom, lightbox) |
|
|
135
|
+
| **Blocks** | `AppShell`, `PageHeader`, `StatCard`, `StatusPill`, `EmptyState`, `ErrorState`, `LoadingState`, `ConfirmDialog`, `BottomActionBar` (+ `BottomActionBar.Spacer`), `ProductCard` (storefront product tile) — page-level patterns; build pages from these before composing layouts by hand |
|
|
136
136
|
| **Charts** (`@dreamtree-org/twreact-ui/charts`, peer `recharts`) | `LineChart`, `BarChart`, `AreaChart`, `DonutChart`, `Sparkline`, `ScatterChart` — NOT exported from the root |
|
|
137
137
|
| **Rich text** (`@dreamtree-org/twreact-ui/rich-text`, peers `@tiptap/react @tiptap/pm @tiptap/starter-kit`) | `RichTextEditor` — NOT exported from the root |
|
|
138
138
|
|
|
@@ -179,7 +179,7 @@ on `Button` or suggesting `tw` prop merging).
|
|
|
179
179
|
|
|
180
180
|
Components use a **shared variant/size/focus vocabulary**:
|
|
181
181
|
|
|
182
|
-
- **Variants:** `primary | secondary | outline | ghost | destructive | success | warning`
|
|
182
|
+
- **Variants:** `primary | secondary | outline | ghost | destructive | success | warning` (`Button` also has `link`, a text link coloured by `tone`)
|
|
183
183
|
(component-specific extras like `Badge: info` and `Alert: neutral` are documented per-component).
|
|
184
184
|
- **Sizes:** `xs | sm | md | lg | xl | 2xl` mapping to heights `h-7 / h-8 / h-10 / h-12 / h-14 / h-16`
|
|
185
185
|
and padding `px-2 / px-3 / px-4 / px-6 / px-8 / px-10` (`Button` implements all six;
|
|
@@ -203,11 +203,12 @@ When suggesting a component or new variant value, use this exact vocabulary.
|
|
|
203
203
|
|
|
204
204
|
**Blocks** are page-level patterns extracted from the products, so screens stop re-inventing them:
|
|
205
205
|
|
|
206
|
-
- `AppShell` — sidebar + top bar + mobile drawer (a left `Sheet`); pass `nav` items and `linkAs={Link}` in Next.js. `bottomNav={<FootNav … />}` adds a phone tab bar (below `md`, spacer included). Fills `min-h-dvh` and pads safe areas.
|
|
206
|
+
- `AppShell` — sidebar + top bar + mobile drawer (a left `Sheet`); pass `nav` items and `linkAs={Link}` in Next.js. `navOpen` / `onNavOpenChange` control the drawer (a FootNav "More" tab can open it — never click the menu button by `aria-label`); `topbarFrom="below-lg"` drops the top bar on desktop. `bottomNav={<FootNav … />}` adds a phone tab bar (below `md`, spacer included). Fills `min-h-dvh` and pads safe areas.
|
|
207
207
|
- `Sidebar` — every item renders as an `<a>`; give items an `href` (and `linkAs={Link}` in Next.js) so Ctrl/Cmd-click opens a new tab. Parents with `href` navigate + expand; the chevron toggles. Mark the current page `active: true` and its group opens on load (control groups with `expandedIds`/`defaultExpandedIds`/`onExpandedChange`). Top-level `group: 'Name'` adds section headings; `badge` (+ `badgeLabel: '3 unread'`) shows counts; `footer` pins links above the user block; `stickyOffset={64}` when a top bar sits above it. The mobile drawer (`isMobileOpen`/`setIsMobileOpen`) is inert when shut, closes on Escape, and opens/closes with an edge swipe (`swipeable`, default on). `collapsible={false}` drops the desktop collapse toggle (the drawer keeps "Close navigation"). Hide drawer items with real CSS if you must — the focus trap skips `display:none` / `visibility:hidden` elements. The content renders once (column on `md`+, drawer below), so don't render a second Sidebar for phones.
|
|
208
208
|
- `PageHeader` — the page's only `h1`, subtitle, one primary action, optional `back` link. `actionsPlacement="bottom"` pins the actions in a `BottomActionBar` on phones (inline on desktop).
|
|
209
209
|
- `BottomActionBar` — sticky bottom bar for a form's / checkout's primary action (`pb-safe`, `z-sticky`, rides above the on-screen keyboard via `visualViewport`). Children are the actions; `fullWidth` / `stackOnMobile` for phone layouts, `align`, `position="fixed" | "sticky"`. Put `<BottomActionBar.Spacer />` at the end of the page. Never hand-roll a `fixed bottom-0` action bar.
|
|
210
|
-
- `StatCard` — KPI tile (`label`, `value`, `icon`, `tone`, `change`).
|
|
210
|
+
- `StatCard` — KPI tile (`label`, `value`, `icon`, `tone`, `change`). Put a `ProgressBar`, a line of text or a `Button variant="link"` in `children` (rendered under the value); `tinted` draws the whole card in `tone` (an overdue card). Don't rebuild a KPI card from `Card`.
|
|
211
|
+
- `ProductCard` — storefront tile: `images` (cover first) + `imageRatio` + `hover` (`zoom` / `second-image`), `eyebrow`, `title`, `meta`, `badges`, `price` / `compareAtPrice` (INR by default, discount computed; `priceFormatter`), `href` / `to` / `as` (the title is the one stretched link), `actions`, `selectable` + `selected` + `onSelectedChange` (compare), `layout="row"` for phone lists.
|
|
211
212
|
- `StatusPill` — map domain states to `{ label, tone }` once; unknown states are humanized, never raw.
|
|
212
213
|
- `EmptyState`, `ErrorState`, `LoadingState` — every data card needs all three.
|
|
213
214
|
- `ConfirmDialog` — use instead of `window.confirm()`; title is the question, button is the verb. Like `Dialog`, it takes `presentation` (`auto` default: bottom sheet on phones, centred modal elsewhere; `popover` = always centred; `sheet` = always a sheet). Write footer actions secondary-then-primary; sheet mode stacks them full width with the primary on top.
|
|
@@ -219,7 +220,7 @@ When suggesting a component or new variant value, use this exact vocabulary.
|
|
|
219
220
|
- `SwipeableListItem` — iOS Mail / Gmail row (`leadingActions` / `trailingActions`: `{ key, label, icon, tone, onSelect }`; `fullSwipe` commits the first action; `open`/`onOpenChange`; `as="li"`). Every action is also reachable through its "More actions" button (menu, action sheet on phones). `Table` uses it for mobile cards via `swipeActions={(row) => ({ leading, trailing })}`. Never hand-roll swipe gestures.
|
|
220
221
|
- `Sheet` — phone sheet (`open`, `onClose(reason)`, `placement` bottom/top/left/right, `snapPoints={[0.5, 1]}` + `activeSnap`/`onSnapChange`, `dismissible`, `title`) with drag-to-dismiss and keyboard-aware positioning; compose `Sheet.Header` / `Sheet.Body` (scrolls) / `Sheet.Footer`. Use it for phone sheets with snap points (filters, pickers, quick forms); keep `Drawer` for record peeks. Never hand-roll drag or `visualViewport` handling.
|
|
221
222
|
- `ActionSheet` — iOS `UIAlertController`-style action sheet on `Sheet` (`open`/`isOpen`, `onClose(reason)`, `title`, `message`, `actions={[{ key, label, icon, tone: 'destructive', onSelect, disabled }]}`, `cancelLabel` default "Cancel"; Cancel always renders separately). Selecting calls `onSelect` then `onClose('select')`; focus returns to the opener. Use it for "what to do with this?" choices on phones instead of a hand-built list in a `Sheet`; for a kebab menu use `ThreeDotPopover`, which already becomes an action sheet on phones.
|
|
222
|
-
- `Button` as a link — `<Button href="tel:…">` or `<Button as={Link} to="/x">`; never copy Button's class string onto an `<a>`.
|
|
223
|
+
- `Button` as a link — `<Button href="tel:…">` or `<Button as={Link} to="/x">`; never copy Button's class string onto an `<a>`. For an inline text action ("All payments →") use `variant="link"` (+ `tone="error"` etc.), not a restyled ghost button.
|
|
223
224
|
- Icon-only `Button` — `<Button size="icon" aria-label="Add"><Plus /></Button>` (`icon-sm`/`icon-lg` too); never a raw `<button>` with an icon. `loading` keeps the button's width.
|
|
224
225
|
- `Badge` — `dot` (live/status dot), `icon` (leading icon slot), `max={99}` with a numeric child renders `99+`.
|
|
225
226
|
- `AvatarGroup` — `<AvatarGroup max={3} size="sm">{people.map(p => <Avatar name={p.name} />)}</AvatarGroup>`; don't hand-roll `-space-x` stacks or a fake `<Avatar name="+5" />`.
|
|
@@ -227,6 +228,9 @@ When suggesting a component or new variant value, use this exact vocabulary.
|
|
|
227
228
|
- `Input` — same `size` (`sm`/`md`/`lg`; `md` is 44px on touch screens); `floatingLabel` puts the label inside the field; `otp` for one-time codes. `type="tel|email|url|search"` already sets `inputMode`/`autoComplete` — don't add them by hand.
|
|
228
229
|
- `Stepper` steps take `title` (or its alias `label`); on phones use `orientation="auto"` (vertical below `sm`) or `variant="dots"` for compact progress. `Tabs variant="segmented"` is the iOS segmented control (2–4 short options); add `swipeable` for swiping between panels on touch. Many tabs scroll sideways on their own, so don't wrap `Tabs.List` in an `overflow-x-auto` div. `Breadcrumbs maxItems={3}` collapses deep trails into "…". `Pagination` collapses to "‹ Page 3 of 10 ›" below `sm` by itself. `Radio` follows the primary token by default — don't pass `radioColor` to match the brand.
|
|
229
230
|
- `SegmentedControl` — radiogroup view toggle (list / board, day / week): `options=[{ value, label, icon?, disabled? }]`, `value`/`defaultValue` + `onChange(value)`, `size` (`sm`/`md`/`lg`, 44px on touch screens), `fullWidth`, `name`, `aria-label`. Arrow keys move and select; the thumb slides. Use `Tabs variant="segmented"` instead when the choice switches panels.
|
|
231
|
+
- `QuantityStepper` — cart / buy-box quantity: `<QuantityStepper label="Quantity" value={qty} min={1} max={stock} onChange={setQty} />` (`onChange` gets a number). `step`, `size`, `loading`, `hideLabel`, `formatValue`, `name`. Never an `Input type="number"` with +/- buttons.
|
|
232
|
+
- `OptionCard` / `OptionCardGroup` — choice tiles: `<OptionCardGroup value={v} onChange={setV} columns={3} aria-label="Template"><OptionCard value="classic" title="Classic" description="…" media={<Sketch />} /></OptionCardGroup>`; `multiple` for a multi-select (array value). Never a row of hand-styled `<button>` tiles.
|
|
233
|
+
- `ImageGallery` — `<ImageGallery images={[{ src, alt, caption? }]} layout="thumbs-side" ratio="4:3" zoom lightbox />`; `index` / `defaultIndex` / `onIndexChange`, `placeholder`, `showCounter`. Use it instead of wiring thumbnails to a `Carousel`.
|
|
230
234
|
- `SearchField` — iOS search input (`type="search"`, Search key, clear button, **Cancel** on phones while focused). `value`/`defaultValue` + `onValueChange(text)`, `onSearch(text)` on Enter, `onCancel`. Pass `results` or `renderResults={({ value, close }) => …}` for a results surface: a full-height Sheet on phones, a popover under the field elsewhere (`presentation` overrides). Never hand-roll a search input with an icon.
|
|
231
235
|
- `Popover` — click-triggered panel with controls (`trigger={<Button>…</Button>}`, children or `({ close }) => …`); `Tooltip` is for read-only hover text only.
|
|
232
236
|
- `Textarea` — same API as `Input` plus `autoResize`/`maxRows`; never a raw `<textarea>`.
|
|
@@ -240,7 +244,7 @@ When suggesting a component or new variant value, use this exact vocabulary.
|
|
|
240
244
|
- `Tour` — onboarding coach marks: `steps=[{ target: '[data-tour="x"]', title, content, placement }]`, `open`, `onClose(reason)`; a missing target falls back to a centred card.
|
|
241
245
|
|
|
242
246
|
**Tokens:** colours are CSS variables (`--dt-primary-600`, `--dt-surface`, …) generated from
|
|
243
|
-
`tokens/tokens.json`. Use semantic classes (`bg-primary-600`, `bg-surface`, `text-foreground`,
|
|
247
|
+
`tokens/tokens.json`. Use semantic classes (`bg-primary-600`, `bg-surface`, `text-foreground`, `text-primary-foreground` on a primary band,
|
|
244
248
|
`text-muted`, `border-line`, `text-micro`, `rounded-card`) — never hex values or Tailwind's raw
|
|
245
249
|
palette (`bg-blue-500`). A product's brand lives in its own repo as `dreamtree.brand.json`;
|
|
246
250
|
`npx @dreamtree-org/twreact-ui brand build --out app/brand.css` turns it into the CSS to load after
|