atlas-react-kit 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/COMPONENTS.md +122 -39
  2. package/DESIGN_RULES.md +61 -17
  3. package/README.md +56 -10
  4. package/dist/accordion.d.ts +8 -0
  5. package/dist/accordion.js +20 -0
  6. package/dist/alert.d.ts +19 -0
  7. package/dist/alert.js +37 -0
  8. package/dist/avatar.d.ts +9 -0
  9. package/dist/avatar.js +16 -0
  10. package/dist/badge.d.ts +2 -2
  11. package/dist/breadcrumb.d.ts +16 -0
  12. package/dist/breadcrumb.js +33 -0
  13. package/dist/button.d.ts +6 -5
  14. package/dist/button.js +6 -5
  15. package/dist/calendar.d.ts +1 -1
  16. package/dist/calendar.js +19 -1
  17. package/dist/combobox.d.ts +4 -1
  18. package/dist/combobox.js +10 -5
  19. package/dist/date-picker.js +14 -3
  20. package/dist/date-time-picker.d.ts +36 -0
  21. package/dist/date-time-picker.js +105 -0
  22. package/dist/dropdown-menu.d.ts +26 -0
  23. package/dist/dropdown-menu.js +56 -0
  24. package/dist/error-boundary.d.ts +1 -1
  25. package/dist/field.js +1 -1
  26. package/dist/form/date-time-picker-field.d.ts +16 -0
  27. package/dist/form/date-time-picker-field.js +12 -0
  28. package/dist/form/text-field.d.ts +7 -1
  29. package/dist/form/text-field.js +3 -3
  30. package/dist/pagination.d.ts +18 -0
  31. package/dist/pagination.js +35 -0
  32. package/dist/progress.d.ts +8 -0
  33. package/dist/progress.js +13 -0
  34. package/dist/radio-group.d.ts +13 -0
  35. package/dist/radio-group.js +20 -0
  36. package/dist/spinner.d.ts +8 -0
  37. package/dist/spinner.js +13 -0
  38. package/dist/switch.d.ts +9 -0
  39. package/dist/switch.js +14 -0
  40. package/dist/table.d.ts +2 -0
  41. package/dist/table.js +25 -1
  42. package/dist/tabs.d.ts +6 -3
  43. package/dist/tabs.js +5 -2
  44. package/dist/theme-provider.d.ts +2 -1
  45. package/dist/theme-provider.js +28 -4
  46. package/dist/toggle.js +1 -1
  47. package/dist/tooltip.d.ts +15 -0
  48. package/dist/tooltip.js +26 -0
  49. package/package.json +58 -5
  50. package/skills/atlas-react-kit/SKILL.md +139 -0
  51. package/theme.css +5 -5
package/COMPONENTS.md CHANGED
@@ -5,39 +5,64 @@ underlying element or Radix props, including `className`, alongside their option
5
5
  Form helpers and `TableBodySkeleton` accept only their documented props. There is
6
6
  no root component barrel.
7
7
 
8
- | Subpath | Main exports and options |
9
- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
10
- | `button` | `Button`, `buttonVariants`, `quietButtonInteraction`; `variant`, `size`, `asChild`; outline/ghost hover is brand purple + selected wash, not muted grey |
11
- | `badge` | `Badge`, `badgeVariants`; `variant`, `tone`, `asChild` |
12
- | `calendar` | `Calendar`, `CalendarDayButton`; React Day Picker props and `buttonVariant` |
13
- | `card` | `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardAction`, `CardContent`, `CardFooter`; `Card` supports `flush`; root uses `min-h-min shrink-0` so flex columns do not collapse it, and `min-w-0` so nested tables scroll; `CardContent className="px-0"` overrides padding without `!` |
14
- | `checkbox` | `Checkbox` |
15
- | `combobox` | `Combobox`; `options` as `{ value, label }[]`, `value`, `onValueChange`; optional placeholder, search, empty text; trigger matches Input/SelectTrigger chrome; keyboard highlight scrolls into view |
16
- | `date-picker` | `DatePickerButton`, `toDate`, `formatLocalDate`, `DateRange`; `mode` `"single"` (default) or `"range"`; single `value` is `Date` or `yyyy-MM-dd`; range `value` is `{ from, to }`; trigger matches Input chrome; optional `numberOfMonths` (range default 1 below `md`, 2 at `md+`) and `side` |
17
- | `dialog` | `Dialog`, trigger, portal, overlay, content, close, header, footer, title, description; `DialogContent` supports `showCloseButton` |
18
- | `empty-panel` | `EmptyPanel`; required `children`; optional `size`, `muted`, `className` |
19
- | `error-boundary` | `ErrorBoundary`, `ErrorFallback`; optional `message`; retry resets, reload refreshes the page |
20
- | `field` | Field, label, legend, description, group, set, title, content, separator, error; `Field` supports `orientation` |
21
- | `input`, `textarea`, `label` | `Input`, `Textarea`, `Label` |
22
- | `popover` | `Popover`, trigger, anchor, content, header, title, description |
23
- | `select` | Select, trigger, value, content, group, label, item, separator, scroll buttons; trigger `size`; trigger text is `text-base md:text-sm` like Input |
24
- | `separator`, `skeleton` | `Separator`, `Skeleton` |
25
- | `sortable-table-head` | `SortableTableHead`; `sorted`, `direction` (`asc` \| `desc`), `onClick`; sets `aria-sort` |
26
- | `sonner` | `Toaster`; Sonner props, with `next-themes` theme defaults |
27
- | `table` | Table, header, body, footer, row, head, cell, caption; root scroller is `min-w-0 overflow-x-auto` with a fade cue; cells are `px-2` below `sm` |
28
- | `table-body-skeleton` | `TableBodySkeleton`; `columns`, optional `rows` (default 5) |
29
- | `tabs` | `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`, `TabsNav`, `tabsListVariants`; list `variant` is `default` or `line` |
30
- | `toggle` | `Toggle`, `toggleVariants`; `variant`, `size` |
31
- | `toggle-group` | `ToggleGroup`, `ToggleGroupItem`; `variant`, `size` |
32
- | `theme-provider` | `ThemeProvider`, `useTheme`; optional `storageKey` (default `atlas-theme`), `enableShortcut` for the `d` toggle |
33
- | `form/text-field` | `FormTextField`, type `TextFieldApi`; controlled field, label, multiline, description, input constraints |
34
- | `form/select-field` | `FormSelectField`; `value`, `onValueChange`, `label`, optional `meta` and placeholder; select item children |
35
- | `form/date-picker-field` | `FormDatePickerField`; `value`, `onValueChange`, `label`, optional `meta`, placeholder, and `className` (forwarded to the trigger) |
36
- | `form/field-error` | `FieldError`, `getFieldErrorMessage`; types `FieldErrorMessage`, `FieldMetaState` |
37
- | `form/feedback-field` | `FormFeedbackField`; nullable `message` |
38
- | `next/tabs-nav-link` | `TabsNavLink`; Next Link props and required `active` boolean; requires Next.js 16 |
39
- | `utils` | `cn`, `onInputChange` |
40
- | `theme.css` | Tailwind 4 theme; installation in [README](./README.md) |
8
+ | Subpath | Main exports and options |
9
+ | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
10
+ | `accordion` | `Accordion`, `AccordionItem`, `AccordionTrigger`, `AccordionContent`; Radix `type` `single` (with `collapsible`) or `multiple` |
11
+ | `alert` | `Alert`, `AlertTitle`, `AlertDescription`, `alertVariants`; `variant` `default`, `success`, `warning`, `destructive`; tone on title and icon, body stays muted; destructive is `role="alert"` |
12
+ | `avatar` | `Avatar`, `AvatarImage`, `AvatarFallback`; `size` `sm`, `default`, `lg`; round by design |
13
+ | `breadcrumb` | `Breadcrumb`, `BreadcrumbList`, `BreadcrumbItem`, `BreadcrumbLink` (`asChild` for router links), `BreadcrumbPage` (`aria-current`), `BreadcrumbSeparator`, `BreadcrumbEllipsis` |
14
+ | `button` | `Button`, `buttonVariants`, `quietButtonInteraction`; `variant`, `size`, `asChild`; outline/ghost hover is brand purple + selected wash, not muted grey |
15
+ | `badge` | `Badge`, `badgeVariants`; `variant`, `tone`, `asChild` |
16
+ | `calendar` | `Calendar`, `CalendarDayButton`; React Day Picker props and `buttonVariant` |
17
+ | `card` | `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardAction`, `CardContent`, `CardFooter`; `Card` supports `flush`; root uses `min-h-min shrink-0` so flex columns do not collapse it, and `min-w-0` so nested tables scroll; `CardContent className="px-0"` overrides padding without `!` |
18
+ | `checkbox` | `Checkbox` |
19
+ | `combobox` | `Combobox`; `options` as `{ value, label }[]`, `value`, `onValueChange`; optional placeholder, search, empty text; trigger matches Input/SelectTrigger chrome; keyboard highlight scrolls into view |
20
+ | `date-picker` | `DatePickerButton`, `toDate`, `formatLocalDate`, `DateRange`; `mode` `"single"` (default) or `"range"`; single `value` is `Date` or `yyyy-MM-dd`; range `value` is `{ from, to }`; trigger matches Input chrome; optional `numberOfMonths` (range default 1 below `md`, 2 at `md+`) and `side` |
21
+ | `date-time-picker` | `DateTimePickerButton`, `resolveTimeStep`, `DEFAULT_TIME_STEP`; controlled `value` is `Date` or `null`; `onChange` emits `Date \ |
22
+ | `dialog` | `Dialog`, trigger, portal, overlay, content, close, header, footer, title, description; `DialogContent` supports `showCloseButton` |
23
+ | `dropdown-menu` | `DropdownMenu` family (trigger, content, item, checkbox/radio items, label, separator, shortcut, sub menus); item `variant="destructive"` and `inset`; for commands, not values |
24
+ | `empty-panel` | `EmptyPanel`; required `children`; optional `size`, `muted`, `className` |
25
+ | `error-boundary` | `ErrorBoundary`, `ErrorFallback`; optional `message`; retry resets, reload refreshes the page |
26
+ | `field` | Field, label, legend, description, group, set, title, content, separator, error; `Field` supports `orientation` |
27
+ | `input`, `textarea`, `label` | `Input`, `Textarea`, `Label` |
28
+ | `pagination` | `Pagination`, `PaginationContent`, `PaginationItem`, `PaginationLink` (`isActive` sets `aria-current` and the selected wash), `PaginationPrevious`, `PaginationNext`, `PaginationEllipsis` |
29
+ | `popover` | `Popover`, trigger, anchor, content, header, title, description |
30
+ | `progress` | `Progress`; Radix props, `value` 0–100; label it with `aria-label` or `aria-labelledby` |
31
+ | `radio-group` | `RadioGroup`, `RadioGroupItem`; short visible lists; arrow keys move the selection; round dots |
32
+ | `select` | Select, trigger, value, content, group, label, item, separator, scroll buttons; trigger `size`; trigger text is `text-base md:text-sm` like Input |
33
+ | `separator`, `skeleton` | `Separator`, `Skeleton` |
34
+ | `sortable-table-head` | `SortableTableHead`; `sorted`, `direction` (`asc` \ |
35
+ | `sonner` | `Toaster`; Sonner props, with `next-themes` theme defaults |
36
+ | `spinner` | `Spinner`; `role="status"` with `aria-label` (default `Loading`) |
37
+ | `switch` | `Switch`; settings that apply immediately; square track and thumb |
38
+ | `table` | Table, header, body, footer, row, head, cell, caption; root scroller is `min-w-0 overflow-x-auto` with a fade cue; cells are `px-2` below `sm` |
39
+ | `table-body-skeleton` | `TableBodySkeleton`; `columns`, optional `rows` (default 5) |
40
+ | `tabs` | `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`, `TabsNav`, `tabsListVariants`, `tabsTriggerVariants`; list `variant` is `default` or `line`; style `TabsNav` links with `tabsTriggerVariants()` |
41
+ | `toggle` | `Toggle`, `toggleVariants`; `variant`, `size` |
42
+ | `toggle-group` | `ToggleGroup`, `ToggleGroupItem`; `variant`, `size` |
43
+ | `theme-provider` | `ThemeProvider`, `useTheme`; optional `storageKey` (default `atlas-theme`), `enableShortcut` for the `d` toggle |
44
+ | `tooltip` | `Tooltip`, `TooltipTrigger`, `TooltipContent`, `TooltipProvider`; opens on hover and focus; short labels only |
45
+ | `form/text-field` | `FormTextField`, type `TextFieldApi`; controlled field, label, multiline, description, input constraints |
46
+ | `form/select-field` | `FormSelectField`; `value`, `onValueChange`, `label`, optional `meta` and placeholder; select item children |
47
+ | `form/date-picker-field` | `FormDatePickerField`; `value`, `onValueChange`, `label`, optional `meta`, placeholder, and `className` (forwarded to the trigger) |
48
+ | `form/date-time-picker-field` | `FormDateTimePickerField`; `value` is `Date` or `null`; `onValueChange` emits `Date \ |
49
+ | `form/field-error` | `FieldError`, `getFieldErrorMessage`; types `FieldErrorMessage`, `FieldMetaState` |
50
+ | `form/feedback-field` | `FormFeedbackField`; nullable `message` |
51
+ | `next/tabs-nav-link` | `TabsNavLink`; Next Link props and required `active` boolean; requires Next.js 16 |
52
+ | `utils` | `cn`, `onInputChange` |
53
+ | `theme.css` | Tailwind 4 theme; installation in [README](./README.md) |
54
+
55
+ ## Status
56
+
57
+ Components are **stable** unless listed here. Stable components change
58
+ through the deprecation policy in
59
+ [CONTRIBUTING.md](https://github.com/Kerubi-5/atlas-design-system/blob/main/CONTRIBUTING.md):
60
+ a deprecation notice first, removal in a later minor release.
61
+
62
+ **Beta** components may still change shape in a minor release; the
63
+ changelog calls out every change: `accordion`, `alert`, `avatar`,
64
+ `breadcrumb`, `date-time-picker`, `dropdown-menu`, `pagination`, `progress`,
65
+ `radio-group`, `spinner`, `switch`, `tooltip`.
41
66
 
42
67
  ## Forms
43
68
 
@@ -57,10 +82,12 @@ type TextFieldApi = {
57
82
 
58
83
  `FormTextField` accepts `field` and `label`; use `multiline` and `rows` for a
59
84
  textarea, `inputType` for a specific input, and `showError={false}` to suppress
60
- error text and invalid styling. `FormSelectField` and `FormDatePickerField`
61
- accept the same error meta shape. `FormDatePickerField` takes a `Date` or
62
- `yyyy-MM-dd` string and calls `onValueChange` with `Date | undefined`.
63
- `FieldError` accepts either `message` or pre-resolved `text`.
85
+ error text and invalid styling. `FormSelectField`, `FormDatePickerField`, and
86
+ `FormDateTimePickerField` accept the same error meta shape.
87
+ `FormDatePickerField` takes a `Date` or `yyyy-MM-dd` string and calls
88
+ `onValueChange` with `Date | undefined`. `FormDateTimePickerField` takes a
89
+ `Date` or `null` and calls `onValueChange` with `Date | null`. `FieldError`
90
+ accepts either `message` or pre-resolved `text`.
64
91
 
65
92
  `DatePickerButton` is single-day by default. For a from/to range, set
66
93
  `mode="range"`; `value` is `{ from, to }` (each side `Date` or `yyyy-MM-dd`)
@@ -72,10 +99,41 @@ it does not collapse to the default popover width, and includes a Clear
72
99
  action. Pass `side` to prefer a placement; Radix still flips on collision.
73
100
  `FormDatePickerField` forwards `className` to that trigger.
74
101
 
102
+ `DateTimePickerButton` is the date-and-time control. `value` is a `Date` or
103
+ `null`; `onChange` receives `Date | null`. The trigger matches the same
104
+ form-control chrome. The popover is one month, a labelled Hour and Minute
105
+ pair (24-hour, stepped by `timeStep`, default 15), and Clear. Picking a day
106
+ keeps the popover open so the time can be set; the time selects stay
107
+ disabled until a day is chosen. The panel is `w-auto`, viewport-clamped, and
108
+ capped at `min(100vw - 2rem, 24rem)` so it stays on-screen at 390px. Pass
109
+ `side` to prefer a placement. `FormDateTimePickerField` forwards `className`
110
+ and `timeStep` to that control.
111
+
75
112
  Outline and ghost `Button` hover and focus use `border-primary`,
76
- `text-primary`, and `bg-selected`. They do not use a muted grey fill, including
113
+ `text-selected-foreground`, and `bg-selected`. They do not use a muted grey fill, including
77
114
  when the control sits on a selected row (for example "Clear selection").
78
115
 
116
+ ```tsx
117
+ import { DateTimePickerButton } from "atlas-react-kit/date-time-picker"
118
+
119
+ export function EventStarts({
120
+ value,
121
+ onChange,
122
+ }: {
123
+ value: Date | null
124
+ onChange: (date: Date | null) => void
125
+ }) {
126
+ return (
127
+ <DateTimePickerButton
128
+ value={value}
129
+ onChange={onChange}
130
+ timeStep={15}
131
+ placeholder="Pick a date and time"
132
+ />
133
+ )
134
+ }
135
+ ```
136
+
79
137
  ```tsx
80
138
  import { DatePickerButton, type DateRange } from "atlas-react-kit/date-picker"
81
139
 
@@ -136,7 +194,32 @@ export function ViewSwitcher({ view }: { view: string }) {
136
194
  }
137
195
  ```
138
196
 
139
- Outside Next, compose `TabsNav` with your own links and active-page semantics.
197
+ Outside Next, compose `TabsNav` with your own links: style each one with
198
+ `tabsTriggerVariants()` and set `data-state="active"` plus `aria-current="page"`
199
+ on the active link.
200
+
201
+ ```tsx
202
+ import { TabsNav, tabsTriggerVariants } from "atlas-react-kit/tabs"
203
+
204
+ export function ViewSwitcher({ view }: { view: string }) {
205
+ return (
206
+ <TabsNav aria-label="Items view">
207
+ {["list", "board"].map((item) => (
208
+ <a
209
+ key={item}
210
+ href={`?view=${item}`}
211
+ data-state={view === item ? "active" : "inactive"}
212
+ aria-current={view === item ? "page" : undefined}
213
+ className={tabsTriggerVariants()}
214
+ >
215
+ {item}
216
+ </a>
217
+ ))}
218
+ </TabsNav>
219
+ )
220
+ }
221
+ ```
222
+
140
223
  Hook-driven controls and form handlers declare their client boundary; presentational
141
224
  modules such as `button`, `badge`, `card`, and `skeleton` can be composed from
142
225
  React Server Components. Pass interactive callbacks from client components.
package/DESIGN_RULES.md CHANGED
@@ -12,16 +12,60 @@ light/dark color pairs; tokens already switch with the theme.
12
12
 
13
13
  Selected or active navigation, tabs, toggles, chips, rows, and labels use
14
14
  `bg-selected text-selected-foreground`. Outline and ghost buttons hover and
15
- focus with `border-primary text-primary` and the selected wash, not a muted
16
- grey fill. Table row hover can stay `hover:bg-muted`. The shipped controls
17
- apply this distinction.
15
+ focus with `border-primary`, `text-selected-foreground`, and the selected wash,
16
+ not a muted grey fill. Table row hover can stay `hover:bg-muted`. The shipped
17
+ controls apply this distinction.
18
+
19
+ For brand-colored text (links, hovered labels), use `text-selected-foreground`:
20
+ it is the primary purple in light mode and a lighter purple in dark mode.
21
+ `text-primary` as text fails contrast on the dark background (about 2:1).
18
22
 
19
23
  Use `success`, `warning`, and `destructive` for good, caution, and bad states.
20
- For tinted status chips, use `Badge variant="soft" tone="success"` or
21
- `tone="warning"`. The destructive tone has a known contrast limitation on its
22
- own light-mode tint; prefer plain `text-destructive` or a solid fill. Status
23
- tones have no paired foreground token; avoid placing text on solid status fills.
24
- Application-specific identity colors belong to the application.
24
+ For tinted status chips, use `Badge variant="soft"` with `tone="success"`,
25
+ `"warning"`, or `"destructive"`; each meets AA as text on the page, a card, and
26
+ its own soft tint in both themes. Status tones have no paired foreground token;
27
+ avoid placing text on solid status fills. Application-specific identity colors
28
+ belong to the application.
29
+
30
+ ## Accessibility
31
+
32
+ The target is WCAG 2.2 AA. CI runs axe-core's WCAG 2.2 A and AA rules against
33
+ every Storybook story in light and dark mode (`npm run test:a11y --prefix
34
+ site`), and a violation fails the build. Automated checks do not replace
35
+ keyboard and screen-reader testing.
36
+
37
+ - Text meets 4.5:1 against its background. The token pairs are tuned for it,
38
+ including `text-muted-foreground` on `bg-muted` and status text on its soft
39
+ tint; raw colors and opacity tricks are where contrast breaks.
40
+ - Every control has an accessible name: a `Label` with `htmlFor`, or
41
+ `aria-label` on icon-only buttons and an unlabelled `Combobox`.
42
+ - Visible text is part of the accessible name, so speech users can say what
43
+ they see (the calendar labels days "September 27", not "27th").
44
+ - Keep the focus rings the kit ships. Content that scrolls must be reachable
45
+ by keyboard; `Table` adds itself to the tab order while it overflows.
46
+
47
+ ## Writing
48
+
49
+ Write labels and messages in sentence case; components that display uppercase
50
+ (buttons, card and dialog titles, badges) apply it with CSS, so screen readers
51
+ still read normal words.
52
+
53
+ - **Buttons** say what happens: a verb and, when needed, a noun ("Save",
54
+ "Export orders", "Delete order"). Avoid "OK", "Submit", and "Click here".
55
+ In a confirmation dialog, the confirm button repeats the action from the
56
+ title ("Delete order?" / "Delete order").
57
+ - **Errors** say what happened and what to do next, in plain words: "Could
58
+ not save. Check your connection and try again." Don't blame the user or
59
+ show only a code. Field errors name the fix: "Enter an email address like
60
+ name@company.com."
61
+ - **Empty states** say what will appear and how to add the first one: "No
62
+ orders yet. Orders you create show up here."
63
+ - **Toasts** confirm in past tense and stay short ("Settings saved"). Anything
64
+ the user must act on belongs in an `Alert` or dialog, not a toast.
65
+ - **Placeholders** show an example, never the label; every field keeps a
66
+ visible `Label`.
67
+ - Skip "please", exclamation marks, and filler ("Successfully saved" is
68
+ "Saved"). Use numerals, and `tabular-nums` where numbers line up in columns.
25
69
 
26
70
  ## Shape and composition
27
71
 
@@ -39,19 +83,19 @@ column, and `min-w-0` so nested tables scroll instead of widening the card.
39
83
  `Dialog` already scrolls inside the viewport on small screens; avoid
40
84
  additional height limits.
41
85
 
42
- `Input`, `SelectTrigger`, `DatePickerButton`, and `Combobox` share form-control
43
- chrome: 40px height, `px-3`, `text-base md:text-sm`, `border-input`, and
44
- `shadow-xs`. Do not restyle those triggers as outline buttons in application
45
- code.
86
+ `Input`, `SelectTrigger`, `DatePickerButton`, `DateTimePickerButton`, and
87
+ `Combobox` share form-control chrome: 40px height, `px-3`,
88
+ `text-base md:text-sm`, `border-input`, and `shadow-xs`. Do not restyle those
89
+ triggers as outline buttons in application code.
46
90
 
47
91
  Use `Tabs` with actual `TabsContent` panels. For URL view switches, use a labelled
48
92
  `TabsNav` with links and `aria-current="page"` on the active link. The optional
49
93
  Next adapter provides `TabsNavLink` with this behavior.
50
94
 
51
- Use `FormTextField`, `FormSelectField`, `FormDatePickerField`, and
52
- `FormFeedbackField` for portable form controls, and `FieldError` for field
53
- messages. `Combobox`, `DatePickerButton` (including `mode="range"`),
54
- `ThemeProvider`, `ErrorBoundary`, and `SortableTableHead` are first-class kit
55
- pieces. Domain data and application shells stay with the application: currency
95
+ Use `FormTextField`, `FormSelectField`, `FormDatePickerField`,
96
+ `FormDateTimePickerField`, and `FormFeedbackField` for portable form controls,
97
+ and `FieldError` for field messages. `Combobox`, `DatePickerButton` (including
98
+ `mode="range"`), `DateTimePickerButton`, `ThemeProvider`, `ErrorBoundary`, and
99
+ `SortableTableHead` are first-class kit pieces. Domain data and application shells stay with the application: currency
56
100
  or timezone option lists, query-library error views, markdown editors, kanban
57
101
  boards, and dashboard widgets.
package/README.md CHANGED
@@ -4,7 +4,7 @@ Shared React components and a Tailwind theme with square corners, semantic
4
4
  colors, and distinct selected states. React 19 and Tailwind 4 are required.
5
5
 
6
6
  ```sh
7
- npm install --save-exact atlas-react-kit@0.4.1
7
+ npm install --save-exact atlas-react-kit@0.5.0
8
8
  ```
9
9
 
10
10
  Import components by subpath:
@@ -42,9 +42,33 @@ with the application.
42
42
 
43
43
  See [design rules](./DESIGN_RULES.md) for shared styling decisions and the
44
44
  [component guide](./COMPONENTS.md) for exports and composition examples. Both
45
- files ship in the npm package. Shared component changes belong in this repository;
45
+ files ship in the npm package.
46
+
47
+ ## AI coding assistants
48
+
49
+ The package also ships an agent skill,
50
+ [`skills/atlas-react-kit/SKILL.md`](./skills/atlas-react-kit/SKILL.md). It
51
+ tells an assistant to read the guides for the installed version, pick kit
52
+ components over hand-rolled markup, style with tokens, and check its work. Link
53
+ it into Claude Code from the app root, so it updates with the package:
54
+
55
+ ```sh
56
+ mkdir -p .claude/skills
57
+ ln -s ../../node_modules/atlas-react-kit/skills/atlas-react-kit .claude/skills/atlas-react-kit
58
+ ```
59
+
60
+ On Windows, or to commit a pinned copy, use
61
+ `cp -r node_modules/atlas-react-kit/skills/atlas-react-kit .claude/skills/`
62
+ instead. For Cursor, Codex, or other agents, point your `AGENTS.md` at
63
+ `node_modules/atlas-react-kit/skills/atlas-react-kit/SKILL.md`. Shared component changes belong in this repository;
46
64
  applications compose their feature UI around the package and upgrade deliberately.
47
65
 
66
+ Public docs and a live playground are at
67
+ [design.querobines.com](https://design.querobines.com). The site is Storybook:
68
+ tokens parsed from `theme.css`, these markdown guides rendered from source, and
69
+ editable component stories with light/dark themes, viewport previews, and
70
+ accessibility checks.
71
+
48
72
  ## Development and releases
49
73
 
50
74
  ```sh
@@ -52,17 +76,36 @@ npm ci
52
76
  npm run check
53
77
  npm test
54
78
  npm pack
79
+ npm ci --prefix site
80
+ npm run build --prefix site
81
+ npm run preview --prefix site
55
82
  ```
56
83
 
57
84
  `tsc` emits individual ESM files and TypeScript declarations. CSS is shipped
58
85
  without compilation and processed by the consuming application's Tailwind build.
59
- The optional Next.js 16 adapter requires Next only when imported. The package
60
- does not bundle React.
61
-
62
- 0.4.0 is already on npm. Bumping the version in `package.json` and
63
- merging to `main` (path-filtered to package files) or running
64
- `workflow_dispatch` publishes through npm OIDC trusted publishing after
65
- check, test, and pack gates. Versions already on npm are skipped.
86
+ The public site is Storybook in `site/`. `npm run build --prefix site`
87
+ typechecks the stories, builds Storybook into `site/dist`, then checks the
88
+ static output. Vercel serves that directory; `/playground` and `/storybook`
89
+ redirect to `/`. The site and Storybook dependencies are not part of the npm
90
+ package. The optional Next.js 16 adapter requires Next only when imported. The
91
+ package does not bundle React.
92
+
93
+ Run `npm run dev --prefix site` (or `npm run storybook --prefix site`) for the
94
+ playground on port 6006. Existing examples live in `stories/` and are imported
95
+ by CSF story files in `site/stories/`, so component tests and previews share the
96
+ same examples. Add Storybook entries there when adding an example; the
97
+ static-output check reports any existing demo that has no Storybook entry.
98
+ Interactive stories use Storybook args so changing a control or interacting with
99
+ the component updates the same value. Configuration is in `site/.storybook/`.
100
+
101
+ The Storybook preview disables the kit's theme shortcut and uses the theme
102
+ toolbar instead. The accessibility panel checks the rendered story; it does not
103
+ replace keyboard or assistive-technology testing.
104
+
105
+ Bumping the version in `package.json` and merging to `main` (path-filtered
106
+ to package files) or running `workflow_dispatch` publishes through npm OIDC
107
+ trusted publishing after check, test, and pack gates. Versions already on npm
108
+ are skipped.
66
109
 
67
110
  If trusted publishing is not configured, pack and publish a validated
68
111
  artifact with the package owner's npm login:
@@ -70,9 +113,12 @@ artifact with the package owner's npm login:
70
113
  ```sh
71
114
  mkdir -p release-artifacts
72
115
  npm pack --pack-destination release-artifacts
73
- npm publish ./release-artifacts/atlas-react-kit-0.4.1.tgz --access public
116
+ npm publish ./release-artifacts/atlas-react-kit-0.5.0.tgz --access public
74
117
  ```
75
118
 
119
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for adding components, the browser
120
+ checks, versioning, and deprecations.
121
+
76
122
  Pin an exact package version in consuming applications. Roll back an upgrade by
77
123
  restoring the previous version and lockfile. License: MIT; upstream notices are
78
124
  preserved in [NOTICE](./NOTICE).
@@ -0,0 +1,8 @@
1
+ import * as React from "react";
2
+ import { Accordion as AccordionPrimitive } from "radix-ui";
3
+ /** Stacked sections that expand in place (FAQs, settings groups). */
4
+ declare function Accordion({ ...props }: React.ComponentProps<typeof AccordionPrimitive.Root>): React.JSX.Element;
5
+ declare function AccordionItem({ className, ...props }: React.ComponentProps<typeof AccordionPrimitive.Item>): React.JSX.Element;
6
+ declare function AccordionTrigger({ className, children, ...props }: React.ComponentProps<typeof AccordionPrimitive.Trigger>): React.JSX.Element;
7
+ declare function AccordionContent({ className, children, ...props }: React.ComponentProps<typeof AccordionPrimitive.Content>): React.JSX.Element;
8
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
@@ -0,0 +1,20 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Accordion as AccordionPrimitive } from "radix-ui";
5
+ import { ChevronDownIcon } from "lucide-react";
6
+ import { cn } from "./utils.js";
7
+ /** Stacked sections that expand in place (FAQs, settings groups). */
8
+ function Accordion({ ...props }) {
9
+ return _jsx(AccordionPrimitive.Root, { "data-slot": "accordion", ...props });
10
+ }
11
+ function AccordionItem({ className, ...props }) {
12
+ return (_jsx(AccordionPrimitive.Item, { "data-slot": "accordion-item", className: cn("border-b last:border-b-0", className), ...props }));
13
+ }
14
+ function AccordionTrigger({ className, children, ...props }) {
15
+ return (_jsx(AccordionPrimitive.Header, { className: "flex", children: _jsxs(AccordionPrimitive.Trigger, { "data-slot": "accordion-trigger", className: cn("flex flex-1 items-start justify-between gap-4 rounded-none py-4 text-left text-sm font-medium transition-colors outline-none hover:text-selected-foreground focus-visible:text-selected-foreground focus-visible:ring-2 focus-visible:ring-ring/30 disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180", className), ...props, children: [children, _jsx(ChevronDownIcon, { className: "pointer-events-none size-4 shrink-0 translate-y-0.5 text-muted-foreground transition-transform duration-200" })] }) }));
16
+ }
17
+ function AccordionContent({ className, children, ...props }) {
18
+ return (_jsx(AccordionPrimitive.Content, { "data-slot": "accordion-content", className: "overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down", ...props, children: _jsx("div", { className: cn("pt-0 pb-4", className), children: children }) }));
19
+ }
20
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
@@ -0,0 +1,19 @@
1
+ import * as React from "react";
2
+ import { type VariantProps } from "class-variance-authority";
3
+ /**
4
+ * Status tone sits on the title and icon. The description stays
5
+ * `text-muted-foreground` on the card face, so body text keeps its contrast
6
+ * (the destructive tint is too faint for text in light mode).
7
+ */
8
+ declare const alertVariants: (props?: ({
9
+ variant?: "default" | "success" | "warning" | "destructive" | null | undefined;
10
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
11
+ /**
12
+ * Inline message about the page or a section (save failed, trial ending).
13
+ * Destructive alerts use `role="alert"` so screen readers announce them;
14
+ * others are `role="status"`. Use a toast for transient confirmations.
15
+ */
16
+ declare function Alert({ className, variant, role, ...props }: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>): React.JSX.Element;
17
+ declare function AlertTitle({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
18
+ declare function AlertDescription({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
19
+ export { Alert, AlertDescription, AlertTitle, alertVariants };
package/dist/alert.js ADDED
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cva } from "class-variance-authority";
4
+ import { cn } from "./utils.js";
5
+ /**
6
+ * Status tone sits on the title and icon. The description stays
7
+ * `text-muted-foreground` on the card face, so body text keeps its contrast
8
+ * (the destructive tint is too faint for text in light mode).
9
+ */
10
+ const alertVariants = cva("relative grid w-full grid-cols-[0_1fr] items-start gap-y-1 rounded-none border bg-card px-4 py-3 text-sm text-card-foreground has-[>svg]:grid-cols-[--spacing(4)_1fr] has-[>svg]:gap-x-3 [&>svg]:size-4 [&>svg]:translate-y-0.5", {
11
+ variants: {
12
+ variant: {
13
+ default: "[&>svg]:text-foreground",
14
+ success: "border-success/40 *:data-[slot=alert-title]:text-success [&>svg]:text-success",
15
+ warning: "border-warning/40 *:data-[slot=alert-title]:text-warning [&>svg]:text-warning",
16
+ destructive: "border-destructive/40 *:data-[slot=alert-title]:text-destructive [&>svg]:text-destructive",
17
+ },
18
+ },
19
+ defaultVariants: {
20
+ variant: "default",
21
+ },
22
+ });
23
+ /**
24
+ * Inline message about the page or a section (save failed, trial ending).
25
+ * Destructive alerts use `role="alert"` so screen readers announce them;
26
+ * others are `role="status"`. Use a toast for transient confirmations.
27
+ */
28
+ function Alert({ className, variant = "default", role, ...props }) {
29
+ return (_jsx("div", { "data-slot": "alert", "data-variant": variant, role: role ?? (variant === "destructive" ? "alert" : "status"), className: cn(alertVariants({ variant }), className), ...props }));
30
+ }
31
+ function AlertTitle({ className, ...props }) {
32
+ return (_jsx("div", { "data-slot": "alert-title", className: cn("col-start-2 min-h-4 text-xs font-semibold tracking-wider uppercase", className), ...props }));
33
+ }
34
+ function AlertDescription({ className, ...props }) {
35
+ return (_jsx("div", { "data-slot": "alert-description", className: cn("col-start-2 grid justify-items-start gap-1 text-sm leading-relaxed text-muted-foreground [&_p]:leading-relaxed", className), ...props }));
36
+ }
37
+ export { Alert, AlertDescription, AlertTitle, alertVariants };
@@ -0,0 +1,9 @@
1
+ import * as React from "react";
2
+ import { Avatar as AvatarPrimitive } from "radix-ui";
3
+ /** Person or workspace image with initials while loading or on error. Round by design. */
4
+ declare function Avatar({ className, size, ...props }: React.ComponentProps<typeof AvatarPrimitive.Root> & {
5
+ size?: "sm" | "default" | "lg";
6
+ }): React.JSX.Element;
7
+ declare function AvatarImage({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Image>): React.JSX.Element;
8
+ declare function AvatarFallback({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Fallback>): React.JSX.Element;
9
+ export { Avatar, AvatarFallback, AvatarImage };
package/dist/avatar.js ADDED
@@ -0,0 +1,16 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Avatar as AvatarPrimitive } from "radix-ui";
5
+ import { cn } from "./utils.js";
6
+ /** Person or workspace image with initials while loading or on error. Round by design. */
7
+ function Avatar({ className, size = "default", ...props }) {
8
+ return (_jsx(AvatarPrimitive.Root, { "data-slot": "avatar", "data-size": size, className: cn("relative flex shrink-0 overflow-hidden rounded-full select-none data-[size=default]:size-8 data-[size=lg]:size-10 data-[size=sm]:size-6", className), ...props }));
9
+ }
10
+ function AvatarImage({ className, ...props }) {
11
+ return (_jsx(AvatarPrimitive.Image, { "data-slot": "avatar-image", className: cn("aspect-square size-full object-cover", className), ...props }));
12
+ }
13
+ function AvatarFallback({ className, ...props }) {
14
+ return (_jsx(AvatarPrimitive.Fallback, { "data-slot": "avatar-fallback", className: cn("flex size-full items-center justify-center rounded-full bg-muted text-xs font-semibold text-muted-foreground uppercase", className), ...props }));
15
+ }
16
+ export { Avatar, AvatarFallback, AvatarImage };
package/dist/badge.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as React from "react";
2
2
  import { type VariantProps } from "class-variance-authority";
3
3
  declare const badgeVariants: (props?: ({
4
- variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | "soft" | null | undefined;
5
- tone?: "destructive" | "neutral" | "success" | "warning" | null | undefined;
4
+ variant?: "default" | "link" | "destructive" | "secondary" | "outline" | "ghost" | "soft" | null | undefined;
5
+ tone?: "success" | "warning" | "destructive" | "neutral" | null | undefined;
6
6
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
7
  declare function Badge({ className, variant, tone, asChild, ...props }: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & {
8
8
  asChild?: boolean;
@@ -0,0 +1,16 @@
1
+ import * as React from "react";
2
+ /**
3
+ * Where the current page sits in a hierarchy. The last item is
4
+ * `BreadcrumbPage` (plain text with `aria-current="page"`), not a link.
5
+ */
6
+ declare function Breadcrumb({ ...props }: React.ComponentProps<"nav">): React.JSX.Element;
7
+ declare function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">): React.JSX.Element;
8
+ declare function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">): React.JSX.Element;
9
+ /** Pass `asChild` to render a router link (e.g. Next `Link`) with these styles. */
10
+ declare function BreadcrumbLink({ asChild, className, ...props }: React.ComponentProps<"a"> & {
11
+ asChild?: boolean;
12
+ }): React.JSX.Element;
13
+ declare function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">): React.JSX.Element;
14
+ declare function BreadcrumbSeparator({ children, className, ...props }: React.ComponentProps<"li">): React.JSX.Element;
15
+ declare function BreadcrumbEllipsis({ className, ...props }: React.ComponentProps<"span">): React.JSX.Element;
16
+ export { Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, };