atlas-react-kit 0.10.0 → 0.11.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 (100) hide show
  1. package/COMPONENTS.md +50 -8
  2. package/DESIGN_RULES.md +12 -0
  3. package/README.md +2 -2
  4. package/dist/accordion.d.ts +8 -1
  5. package/dist/accordion.js +8 -3
  6. package/dist/alert.js +1 -1
  7. package/dist/avatar.d.ts +5 -1
  8. package/dist/avatar.js +7 -2
  9. package/dist/badge.js +1 -1
  10. package/dist/breadcrumb.js +3 -2
  11. package/dist/button.js +6 -6
  12. package/dist/calendar.js +19 -12
  13. package/dist/card.d.ts +1 -1
  14. package/dist/checkbox.js +2 -2
  15. package/dist/color-legend.d.ts +6 -4
  16. package/dist/color-legend.js +7 -4
  17. package/dist/combobox.d.ts +7 -2
  18. package/dist/combobox.js +35 -25
  19. package/dist/date-picker.d.ts +7 -13
  20. package/dist/date-picker.js +14 -20
  21. package/dist/date-time-picker.d.ts +5 -13
  22. package/dist/date-time-picker.js +26 -30
  23. package/dist/dialog.d.ts +5 -1
  24. package/dist/dialog.js +4 -3
  25. package/dist/dropdown-menu.js +5 -4
  26. package/dist/empty-panel.d.ts +4 -3
  27. package/dist/empty-panel.js +3 -3
  28. package/dist/error-boundary.d.ts +5 -3
  29. package/dist/error-boundary.js +9 -4
  30. package/dist/field.d.ts +14 -1
  31. package/dist/field.js +16 -3
  32. package/dist/form/date-picker-field.d.ts +8 -2
  33. package/dist/form/date-picker-field.js +4 -4
  34. package/dist/form/date-time-picker-field.d.ts +8 -1
  35. package/dist/form/date-time-picker-field.js +3 -3
  36. package/dist/form/feedback-field.d.ts +2 -1
  37. package/dist/form/feedback-field.js +3 -2
  38. package/dist/form/field-error.d.ts +4 -1
  39. package/dist/form/field-error.js +3 -2
  40. package/dist/form/field-shell.d.ts +25 -1
  41. package/dist/form/field-shell.js +36 -3
  42. package/dist/form/markdown-field.d.ts +7 -1
  43. package/dist/form/markdown-field.js +3 -3
  44. package/dist/form/select-field.d.ts +7 -1
  45. package/dist/form/select-field.js +3 -3
  46. package/dist/form/text-field.d.ts +5 -1
  47. package/dist/form/text-field.js +6 -3
  48. package/dist/info-tip.d.ts +2 -2
  49. package/dist/info-tip.js +34 -16
  50. package/dist/internal/form-control.d.ts +5 -0
  51. package/dist/internal/form-control.js +7 -0
  52. package/dist/internal/listbox-navigation.d.ts +32 -0
  53. package/dist/internal/listbox-navigation.js +38 -0
  54. package/dist/internal/listbox-option.d.ts +25 -0
  55. package/dist/internal/listbox-option.js +23 -0
  56. package/dist/internal/math.d.ts +5 -0
  57. package/dist/internal/math.js +7 -0
  58. package/dist/internal/overlay-styles.d.ts +28 -0
  59. package/dist/internal/overlay-styles.js +28 -0
  60. package/dist/internal/styles.d.ts +27 -0
  61. package/dist/internal/styles.js +27 -0
  62. package/dist/internal/tabs-styles.d.ts +10 -1
  63. package/dist/internal/tabs-styles.js +27 -5
  64. package/dist/lazy-calendar.js +9 -1
  65. package/dist/markdown.js +10 -4
  66. package/dist/meter.d.ts +3 -2
  67. package/dist/meter.js +6 -3
  68. package/dist/pagination.d.ts +8 -1
  69. package/dist/pagination.js +15 -9
  70. package/dist/picker-shell.d.ts +59 -0
  71. package/dist/picker-shell.js +21 -0
  72. package/dist/popover.js +2 -1
  73. package/dist/progress.d.ts +7 -3
  74. package/dist/progress.js +10 -4
  75. package/dist/radio-group.js +2 -2
  76. package/dist/search-picker.d.ts +4 -6
  77. package/dist/search-picker.js +28 -33
  78. package/dist/select.js +12 -6
  79. package/dist/separator.d.ts +4 -0
  80. package/dist/separator.js +5 -1
  81. package/dist/slider.d.ts +8 -3
  82. package/dist/slider.js +7 -5
  83. package/dist/sortable-table-head.d.ts +3 -1
  84. package/dist/sortable-table-head.js +3 -2
  85. package/dist/stat-tile.d.ts +6 -4
  86. package/dist/stat-tile.js +6 -4
  87. package/dist/switch.js +2 -2
  88. package/dist/table-body-skeleton.d.ts +8 -2
  89. package/dist/table-body-skeleton.js +7 -3
  90. package/dist/table.d.ts +7 -1
  91. package/dist/table.js +6 -2
  92. package/dist/tabs.d.ts +13 -3
  93. package/dist/tabs.js +29 -10
  94. package/dist/theme-provider.d.ts +2 -1
  95. package/dist/theme-provider.js +1 -1
  96. package/dist/toggle.js +4 -4
  97. package/dist/tooltip.d.ts +7 -3
  98. package/dist/tooltip.js +17 -6
  99. package/package.json +1 -1
  100. package/theme.css +43 -50
package/COMPONENTS.md CHANGED
@@ -37,11 +37,11 @@ theme from `next-themes`, so toast consumers install that too.
37
37
  | `button` | `Button`, `buttonVariants`, `quietButtonInteraction`; `variant`, `size`, `asChild`; outline/ghost hover is brand purple + selected wash, not muted grey |
38
38
  | `badge` | `Badge`, `badgeVariants`; `variant`, `tone`, `asChild` |
39
39
  | `calendar` | `Calendar`, `CalendarDayButton`; React Day Picker props and `buttonVariant` |
40
- | `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 `!` |
40
+ | `card` | `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardAction`, `CardContent`, `CardFooter`; `Card` supports `flush`; `CardTitle as="h2"` (up to `h4`) makes it a section heading; 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 `!` |
41
41
  | `checkbox` | `Checkbox` |
42
42
  | `color-legend` | `ColorLegend`; `title`, `items` as `{ color, range?, label }[]`, optional `caption`, `children`, `aria-label`, `className` (overlay chrome). No attribution. |
43
43
  | `combobox` | `Combobox`; `options` as `{ value, label }[]`, `value`, `onValueChange`; optional placeholder, search, empty text, `creatable`, `normalizeCreateValue` (query → value or `null`), `createLabel`; trigger matches Input/SelectTrigger chrome; keyboard highlight scrolls into view; create is a listbox option selected with Enter |
44
- | `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` |
44
+ | `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+`), `side`, `aria-labelledby`, and `aria-describedby` |
45
45
  | `date-time-picker` | `DateTimePickerButton`, `resolveTimeStep`, `DEFAULT_TIME_STEP`; controlled `value` is `Date` or `null`; `onChange` emits `Date \ |
46
46
  | `dialog` | `Dialog`, trigger, portal, overlay, content, close, header, footer, title, description; `DialogContent` supports `showCloseButton` |
47
47
  | `dropdown-menu` | `DropdownMenu` family (trigger, content, item, checkbox/radio items, label, separator, shortcut, sub menus); item `variant="destructive"` and `inset`; for commands, not values |
@@ -52,7 +52,7 @@ theme from `next-themes`, so toast consumers install that too.
52
52
  | `input`, `textarea`, `label` | `Input`, `Textarea`, `Label` |
53
53
  | `markdown` | `Markdown`, `MarkdownContent`; `content`, optional `className`. GFM via `react-markdown` + `remark-gfm` in this subpath only. Sanitized (no raw HTML; http/https/mailto/#/`/` URLs). Token styles, no typography plugin. |
54
54
  | `meter` | `Meter`; `value`, `max`, optional `min` (default 0), `fillClassName`, `markers` as `{ position, className? }[]`, required `aria-label`, `className`. `role="meter"`. Not `Progress`. |
55
- | `pagination` | `Pagination`, `PaginationContent`, `PaginationItem`, `PaginationLink` (`isActive` sets `aria-current`, the selected wash, and a selected border), `PaginationPrevious`, `PaginationNext`, `PaginationEllipsis` |
55
+ | `pagination` | `Pagination`, `PaginationContent`, `PaginationItem`, `PaginationLink` (`isActive` sets `aria-current`, the selected wash, and a selected border; `disabled` drops the `href` for a page that does not exist, such as Previous on page 1), `PaginationPrevious`, `PaginationNext` (custom text becomes the link name), `PaginationEllipsis` |
56
56
  | `popover` | `Popover`, trigger, anchor, content, header, title, description |
57
57
  | `progress` | `Progress`; Radix props, `value` 0–100; label it with `aria-label` or `aria-labelledby`. For a value against a scale with ticks, use `Meter`. |
58
58
  | `radio-group` | `RadioGroup`, `RadioGroupItem`; short visible lists; arrow keys move the selection; round dots |
@@ -77,7 +77,7 @@ theme from `next-themes`, so toast consumers install that too.
77
77
  | `form/select-field` | `FormSelectField`; `value`, `onValueChange`, `label`, optional `meta` and placeholder; select item children |
78
78
  | `form/date-picker-field` | `FormDatePickerField`; `value`, `onValueChange`, `label`, optional `meta`, placeholder, and `className` (forwarded to the trigger) |
79
79
  | `form/date-time-picker-field` | `FormDateTimePickerField`; `value` is `Date` or `null`; `onValueChange` emits `Date \ |
80
- | `form/field-error` | `FieldError`, `getFieldErrorMessage`; types `FieldErrorMessage`, `FieldMetaState` |
80
+ | `form/field-error` | `FieldError` (optional `id`), `getFieldErrorMessage`; types `FieldErrorMessage`, `FieldMetaState` |
81
81
  | `form/feedback-field` | `FormFeedbackField`; nullable `message` |
82
82
  | `next/tabs-nav-link` | `TabsNavLink`; Next Link props and required `active` boolean; requires Next.js 16 |
83
83
  | `utils` | `cn`, `onInputChange` |
@@ -96,6 +96,34 @@ changelog calls out every change: `accordion`, `alert`, `avatar`,
96
96
  `markdown`, `meter`, `pagination`, `progress`, `radio-group`, `search-picker`,
97
97
  `slider`, `spinner`, `stat-tile`, `switch`, `tooltip`.
98
98
 
99
+ ## Styling parts
100
+
101
+ Every component takes `className` for its root and passes other HTML props
102
+ through to it. Where a part sits inside a component, a prop names it, and
103
+ each part carries a `data-slot` for parent selectors such as
104
+ `*:data-[slot=stat-tile-value]:text-2xl`.
105
+
106
+ | Component | Part props | Slots |
107
+ | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
108
+ | `DialogContent` | `overlayClassName`, `closeButtonClassName` | `dialog-close-button` (the built-in X; your own `DialogClose` keeps `dialog-close`) |
109
+ | `SelectTrigger` | a `className` height replaces `size` | `select-icon`, `select-viewport`, `select-item-indicator` |
110
+ | `TooltipContent` | `arrowClassName` | `tooltip-arrow` |
111
+ | `Combobox` | `contentClassName` (panel, default trigger width), `listClassName`; button props go to trigger | `combobox-search`, `combobox-list`, `combobox-option` |
112
+ | `SearchPicker` | `listClassName`; div props go to the root | `search-picker-option`, `search-picker-empty` |
113
+ | `InfoTip` | `contentClassName`; button props go to the trigger | |
114
+ | `DatePickerButton`, `DateTimePickerButton` | `contentClassName`, `calendarProps` (disabled days, `startMonth`, `endMonth`, `weekStartsOn`, `locale`) | `date-picker-value`, `date-picker-footer`, `date-time-picker-time` |
115
+ | `form/*` fields | `fieldClassName` (wrapper); `className` styles the control; `FormSelectField` `contentClassName`; `FormMarkdownField` `previewClassName` | `form-field`, `form-field-header`, `form-field-description`, `form-field-error`, `form-feedback`, `form-markdown-preview` |
116
+ | `Table` | `containerClassName` (the scroll box, e.g. `max-h-96 overflow-y-auto`); `className` styles the `<table>` | |
117
+ | `TabsList`, `TabsNav` | `scrollerClassName`; `TabsNav` `listClassName`; a list `className` height replaces `h-11` | `tabs-scroller` |
118
+ | `SortableTableHead` | `buttonClassName` | `sortable-table-head-button` |
119
+ | `AccordionTrigger`, `AccordionContent` | `headerClassName`; `AccordionContent` `className` styles the inner box | `accordion-header`, `accordion-content-inner` |
120
+ | `Progress` | `indicatorClassName` | `progress-indicator` |
121
+ | `Meter` | `fillClassName` | `meter-fill`, `meter-marker` |
122
+ | `StatTile` | | `stat-tile-label`, `stat-tile-value`, `stat-tile-sub` |
123
+ | `ColorLegend` | | `color-legend-title`, `-list`, `-item`, `-swatch`, `-range`, `-label`, `-caption` |
124
+ | `RadioGroupItem` | | `radio-group-dot` |
125
+ | `EmptyPanel`, `ErrorFallback`, `TableBodySkeleton` | `className` | `empty-panel`, `error-boundary` |
126
+
99
127
  ## Forms
100
128
 
101
129
  `TextFieldApi` is a structural interface, with no form library dependency:
@@ -120,7 +148,12 @@ for a Write / Preview GFM body. `FormSelectField`, `FormDatePickerField`, and
120
148
  `FormDatePickerField` takes a `Date` or `yyyy-MM-dd` string and calls
121
149
  `onValueChange` with `Date | undefined`. `FormDateTimePickerField` takes a
122
150
  `Date` or `null` and calls `onValueChange` with `Date | null`. `FieldError`
123
- accepts either `message` or pre-resolved `text`.
151
+ accepts either `message` or pre-resolved `text`, and an `id` to point the
152
+ control's `aria-describedby` at. Every `form/*` field does that for you: the
153
+ error text (and `FormTextField`'s `description`) is the control's accessible
154
+ description. The date fields label their trigger with `aria-labelledby` (the
155
+ label, then the trigger) rather than `<label for>`, so the picked date is
156
+ part of its name.
124
157
 
125
158
  `DatePickerButton` is single-day by default. For a from/to range, set
126
159
  `mode="range"`; `value` is `{ from, to }` (each side `Date` or `yyyy-MM-dd`)
@@ -130,6 +163,12 @@ one month below `md` and two months at `md+` unless `numberOfMonths` is set,
130
163
  clamps to the viewport with collision padding and scroll, stays `w-auto` so
131
164
  it does not collapse to the default popover width, and includes a Clear
132
165
  action. Pass `side` to prefer a placement; Radix still flips on collision.
166
+ The popover is a dialog named "Choose date" ("Choose dates" in range mode),
167
+ and focus moves to the selected day, or today, when it opens. Pass
168
+ `aria-labelledby` and `aria-describedby` to name and describe the trigger;
169
+ to keep the date in its name, list the label's id and then `${id}-value`,
170
+ the trigger text's id (a `<label for>` alone names the button by the label
171
+ only).
133
172
  `FormDatePickerField` forwards `className` to that trigger.
134
173
 
135
174
  `DateTimePickerButton` is the date-and-time control. `value` is a `Date` or
@@ -139,7 +178,9 @@ pair (24-hour, stepped by `timeStep`, default 15), and Clear. Picking a day
139
178
  keeps the popover open so the time can be set; the time selects stay
140
179
  disabled until a day is chosen. The panel is `w-auto`, sized by the
141
180
  calendar, and clamped to the viewport so it stays on-screen at 390px. Pass
142
- `side` to prefer a placement. `FormDateTimePickerField` forwards `className`
181
+ `side` to prefer a placement. Like `DatePickerButton`, the popover is a
182
+ named dialog ("Choose date and time") that focuses the selected day or
183
+ today, and the trigger takes `aria-labelledby` and `aria-describedby`. `FormDateTimePickerField` forwards `className`
143
184
  and `timeStep` to that control.
144
185
 
145
186
  Outline and ghost `Button` hover uses `border-selected-foreground`,
@@ -205,8 +246,9 @@ export function Providers({ children }: { children: React.ReactNode }) {
205
246
  ```
206
247
 
207
248
  `ThemeProvider` is a client component and does not import Next.js. It stores the
208
- choice under `atlas-theme` unless `storageKey` is set. Pass `enableShortcut={false}`
209
- to disable the `d` dark/light toggle.
249
+ choice under `atlas-theme` unless `storageKey` is set. The `d` dark/light toggle
250
+ is off by default; pass `enableShortcut` to turn it on, and give people a setting
251
+ to turn it off again, since a single-key shortcut can fire by accident (WCAG 2.1.4).
210
252
 
211
253
  ## URL navigation in Next.js
212
254
 
package/DESIGN_RULES.md CHANGED
@@ -121,6 +121,18 @@ flex row; add `w-full` to stretch it there. `Tooltip` keeps a `max-w-xs` reading
121
121
  `min-h-16` starting height, and toasts their 356px width; override the first
122
122
  two with `className`.
123
123
 
124
+ Parents do not force sizes on their children. A vertical `Field` stretches
125
+ its controls through the flex column, so a control's own width class (such
126
+ as `w-32`) wins; `Field`, `Alert`, and `Pagination` set no width of their
127
+ own. `FieldGroup` keeps `w-full` because its inline-size container has no
128
+ intrinsic width. Size classes that `className` should be able to replace are
129
+ plain classes picked from props (`Avatar` `size`, `Separator` orientation,
130
+ `SelectTrigger` `size`, tab orientation), never `data-*` variants, which sort
131
+ after plain classes and would win. Every part an app may want to style is
132
+ reachable: through `className` on the part, a named `*ClassName` prop when the
133
+ part sits inside a component (listed in COMPONENTS.md under "Styling parts"),
134
+ or its `data-slot`.
135
+
124
136
  `Input`, `SelectTrigger`, `DatePickerButton`, `DateTimePickerButton`, and
125
137
  `Combobox` share form-control chrome: 40px height, `px-3`,
126
138
  `text-base md:text-sm`, `border-input`, and `shadow-xs`. Do not restyle those
package/README.md CHANGED
@@ -8,7 +8,7 @@ React 19, Tailwind 4, and Node >=20.19 are required. Next 16 is needed only
8
8
  when using `next/tabs-nav-link`.
9
9
 
10
10
  ```sh
11
- npm install --save-exact atlas-react-kit@0.10.0
11
+ npm install --save-exact atlas-react-kit@0.11.0
12
12
  ```
13
13
 
14
14
  Import components by subpath:
@@ -128,7 +128,7 @@ artifact with the package owner's npm login:
128
128
  ```sh
129
129
  mkdir -p release-artifacts
130
130
  npm pack --pack-destination release-artifacts
131
- npm publish ./release-artifacts/atlas-react-kit-0.10.0.tgz --access public
131
+ npm publish ./release-artifacts/atlas-react-kit-0.11.0.tgz --access public
132
132
  ```
133
133
 
134
134
  Pin an exact package version in consuming applications. Roll back an upgrade by
@@ -3,6 +3,13 @@ import { Accordion as AccordionPrimitive } from "radix-ui";
3
3
  /** Stacked sections that expand in place (FAQs, settings groups). */
4
4
  declare function Accordion({ ...props }: React.ComponentProps<typeof AccordionPrimitive.Root>): React.JSX.Element;
5
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;
6
+ /** `headerClassName` styles the heading wrapper (`data-slot="accordion-header"`). */
7
+ declare function AccordionTrigger({ className, headerClassName, children, ...props }: React.ComponentProps<typeof AccordionPrimitive.Trigger> & {
8
+ headerClassName?: string;
9
+ }): React.JSX.Element;
10
+ /**
11
+ * `className` styles the padded inner box (`data-slot="accordion-content-inner"`);
12
+ * the outer region animates height and clips.
13
+ */
7
14
  declare function AccordionContent({ className, children, ...props }: React.ComponentProps<typeof AccordionPrimitive.Content>): React.JSX.Element;
8
15
  export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
package/dist/accordion.js CHANGED
@@ -12,10 +12,15 @@ function Accordion({ ...props }) {
12
12
  function AccordionItem({ className, ...props }) {
13
13
  return (_jsx(AccordionPrimitive.Item, { "data-slot": "accordion-item", className: cn("border-b last:border-b-0", className), ...props }));
14
14
  }
15
- function AccordionTrigger({ className, children, ...props }) {
16
- return (_jsx(AccordionPrimitive.Header, { className: "flex", children: _jsxs(AccordionPrimitive.Trigger, { "data-slot": "accordion-trigger", className: cn(focusRing, disabledState, "flex flex-1 items-start justify-between gap-4 rounded-none py-4 text-left text-sm font-medium transition-colors hover:text-selected-foreground focus-visible:text-selected-foreground [&[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" })] }) }));
15
+ /** `headerClassName` styles the heading wrapper (`data-slot="accordion-header"`). */
16
+ function AccordionTrigger({ className, headerClassName, children, ...props }) {
17
+ return (_jsx(AccordionPrimitive.Header, { "data-slot": "accordion-header", className: cn("flex", headerClassName), children: _jsxs(AccordionPrimitive.Trigger, { "data-slot": "accordion-trigger", className: cn(focusRing, disabledState, "flex flex-1 items-start justify-between gap-4 rounded-none py-4 text-left text-sm font-medium transition-colors hover:text-selected-foreground focus-visible:text-selected-foreground [&[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" })] }) }));
17
18
  }
19
+ /**
20
+ * `className` styles the padded inner box (`data-slot="accordion-content-inner"`);
21
+ * the outer region animates height and clips.
22
+ */
18
23
  function AccordionContent({ className, children, ...props }) {
19
- 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 }) }));
24
+ 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", { "data-slot": "accordion-content-inner", className: cn("pt-0 pb-4", className), children: children }) }));
20
25
  }
21
26
  export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
package/dist/alert.js CHANGED
@@ -7,7 +7,7 @@ import { cn } from "./utils.js";
7
7
  * `text-muted-foreground` on the card face, so body text keeps its contrast
8
8
  * (the destructive tint is too faint for text in light mode).
9
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", {
10
+ const alertVariants = cva("relative grid 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
11
  variants: {
12
12
  variant: {
13
13
  default: "[&>svg]:text-foreground",
package/dist/avatar.d.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  import * as React from "react";
2
2
  import { Avatar as AvatarPrimitive } from "radix-ui";
3
- /** Person or workspace image with initials while loading or on error. Round by design. */
3
+ /**
4
+ * Person or workspace image with initials while loading or on error. Round
5
+ * by design. `size` picks a plain size class, so `className="size-12"`
6
+ * replaces it.
7
+ */
4
8
  declare function Avatar({ className, size, ...props }: React.ComponentProps<typeof AvatarPrimitive.Root> & {
5
9
  size?: "sm" | "default" | "lg";
6
10
  }): React.JSX.Element;
package/dist/avatar.js CHANGED
@@ -3,9 +3,14 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { Avatar as AvatarPrimitive } from "radix-ui";
5
5
  import { cn } from "./utils.js";
6
- /** Person or workspace image with initials while loading or on error. Round by design. */
6
+ const avatarSizes = { sm: "size-6", default: "size-8", lg: "size-10" };
7
+ /**
8
+ * Person or workspace image with initials while loading or on error. Round
9
+ * by design. `size` picks a plain size class, so `className="size-12"`
10
+ * replaces it.
11
+ */
7
12
  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 }));
13
+ return (_jsx(AvatarPrimitive.Root, { "data-slot": "avatar", "data-size": size, className: cn("relative flex shrink-0 overflow-hidden rounded-full select-none", avatarSizes[size], className), ...props }));
9
14
  }
10
15
  function AvatarImage({ className, ...props }) {
11
16
  return (_jsx(AvatarPrimitive.Image, { "data-slot": "avatar-image", className: cn("aspect-square size-full object-cover", className), ...props }));
package/dist/badge.js CHANGED
@@ -7,7 +7,7 @@ import { cn } from "./utils.js";
7
7
  const badgeVariants = cva([
8
8
  focusRing,
9
9
  invalidState,
10
- "group/badge inline-flex w-fit shrink-0 items-center justify-center gap-1.5 overflow-hidden rounded-none border-0 bg-transparent px-0 py-0 text-[0.625rem] font-semibold tracking-widest whitespace-nowrap uppercase transition-colors has-data-[icon=inline-end]:pr-0 has-data-[icon=inline-start]:pl-0 [&>svg]:pointer-events-none [&>svg]:size-3!",
10
+ "group/badge inline-flex w-fit shrink-0 items-center justify-center gap-1.5 overflow-hidden rounded-none border-0 bg-transparent px-0 py-0 text-[0.625rem] font-semibold tracking-widest whitespace-nowrap uppercase transition-colors has-data-[icon=inline-end]:pr-0 has-data-[icon=inline-start]:pl-0 [&>svg]:pointer-events-none [&>svg:not([class*='size-'])]:size-3",
11
11
  ], {
12
12
  variants: {
13
13
  variant: {
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { Slot } from "radix-ui";
4
4
  import { ChevronRightIcon, MoreHorizontalIcon } from "lucide-react";
5
+ import { focusRing } from "./internal/styles.js";
5
6
  import { cn } from "./utils.js";
6
7
  /**
7
8
  * Where the current page sits in a hierarchy. The last item is
@@ -19,7 +20,7 @@ function BreadcrumbItem({ className, ...props }) {
19
20
  /** Pass `asChild` to render a router link (e.g. Next `Link`) with these styles. */
20
21
  function BreadcrumbLink({ asChild, className, ...props }) {
21
22
  const Comp = asChild ? Slot.Root : "a";
22
- return (_jsx(Comp, { "data-slot": "breadcrumb-link", className: cn("transition-colors outline-none hover:text-selected-foreground focus-visible:text-selected-foreground focus-visible:underline focus-visible:underline-offset-4", className), ...props }));
23
+ return (_jsx(Comp, { "data-slot": "breadcrumb-link", className: cn(focusRing, "transition-colors hover:text-selected-foreground focus-visible:text-selected-foreground", className), ...props }));
23
24
  }
24
25
  function BreadcrumbPage({ className, ...props }) {
25
26
  return (_jsx("span", { "data-slot": "breadcrumb-page", "aria-current": "page", className: cn("font-medium text-foreground", className), ...props }));
@@ -28,6 +29,6 @@ function BreadcrumbSeparator({ children, className, ...props }) {
28
29
  return (_jsx("li", { "data-slot": "breadcrumb-separator", role: "presentation", "aria-hidden": "true", className: cn("[&>svg]:size-3.5", className), ...props, children: children ?? _jsx(ChevronRightIcon, {}) }));
29
30
  }
30
31
  function BreadcrumbEllipsis({ className, ...props }) {
31
- return (_jsxs("span", { "data-slot": "breadcrumb-ellipsis", role: "presentation", "aria-hidden": "true", className: cn("flex size-6 items-center justify-center", className), ...props, children: [_jsx(MoreHorizontalIcon, { className: "size-4" }), _jsx("span", { className: "sr-only", children: "More" })] }));
32
+ return (_jsxs("span", { "data-slot": "breadcrumb-ellipsis", className: cn("flex size-6 items-center justify-center", className), ...props, children: [_jsx(MoreHorizontalIcon, { className: "size-4", "aria-hidden": "true" }), _jsx("span", { className: "sr-only", children: "More" })] }));
32
33
  }
33
34
  export { Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, };
package/dist/button.js CHANGED
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { cva } from "class-variance-authority";
4
4
  import { Slot } from "radix-ui";
5
- import { disabledState, focusRing, invalidState, selectedState, } from "./internal/styles.js";
5
+ import { controlSize, disabledState, focusRing, invalidState, selectedState, } from "./internal/styles.js";
6
6
  import { cn } from "./utils.js";
7
7
  /**
8
8
  * Outline and ghost hover/focus: selected-foreground border, selected wash, and
@@ -24,17 +24,17 @@ const buttonVariants = cva([
24
24
  variants: {
25
25
  variant: {
26
26
  default: "bg-primary text-primary-foreground hover:bg-primary/80",
27
- outline: `border-border bg-transparent ${quietButtonInteraction}`,
27
+ outline: `border-input bg-transparent ${quietButtonInteraction}`,
28
28
  secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
29
29
  ghost: quietButtonInteraction,
30
- destructive: "bg-destructive/10 text-destructive hover:bg-destructive/15 dark:bg-destructive/20 dark:hover:bg-destructive/30",
30
+ destructive: "bg-destructive/10 text-destructive hover:bg-destructive/15 dark:bg-destructive/20 dark:hover:bg-destructive/25",
31
31
  link: "text-selected-foreground underline underline-offset-4 hover:underline",
32
32
  },
33
33
  size: {
34
- default: "h-10 gap-1.5 px-6 has-data-[icon=inline-end]:pr-4 has-data-[icon=inline-start]:pl-4",
34
+ default: `${controlSize.default} gap-1.5`,
35
35
  xs: "h-7 gap-1 px-3 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
36
- sm: "h-9 gap-1 px-4 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
37
- lg: "h-11 gap-1.5 px-8 has-data-[icon=inline-end]:pr-5 has-data-[icon=inline-start]:pl-5",
36
+ sm: `${controlSize.sm} gap-1`,
37
+ lg: `${controlSize.lg} gap-1.5`,
38
38
  icon: "size-10",
39
39
  "icon-xs": "size-7 [&_svg:not([class*='size-'])]:size-3",
40
40
  "icon-sm": "size-9",
package/dist/calendar.js CHANGED
@@ -4,9 +4,19 @@ import * as React from "react";
4
4
  import { DayPicker, getDefaultClassNames, } from "react-day-picker";
5
5
  import { cn } from "./utils.js";
6
6
  import { Button, buttonVariants } from "./button.js";
7
- import { ChevronLeftIcon, ChevronRightIcon, ChevronDownIcon, } from "lucide-react";
7
+ import { ariaDisabledState } from "./internal/styles.js";
8
+ import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, } from "lucide-react";
9
+ /** Previous / next month buttons share one look. */
10
+ const navButtonClassName = cn("size-(--cell-size) p-0 select-none", ariaDisabledState);
11
+ const chevronIcons = {
12
+ left: ChevronLeftIcon,
13
+ right: ChevronRightIcon,
14
+ up: ChevronUpIcon,
15
+ down: ChevronDownIcon,
16
+ };
8
17
  function Calendar({ className, classNames, showOutsideDays = true, captionLayout = "label", buttonVariant = "ghost", locale, formatters, labels, components, ...props }) {
9
18
  const defaultClassNames = getDefaultClassNames();
19
+ const navButton = cn(buttonVariants({ variant: buttonVariant }), navButtonClassName);
10
20
  return (_jsx(DayPicker, { showOutsideDays: showOutsideDays, className: cn("group/calendar bg-background p-3 [--cell-radius:0] [--cell-size:--spacing(8)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent", String.raw `rtl:**:[.rdp-button\_next>svg]:rotate-180`, String.raw `rtl:**:[.rdp-button\_previous>svg]:rotate-180`, className), captionLayout: captionLayout, locale: locale, formatters: {
11
21
  formatMonthDropdown: (date) => date.toLocaleString(locale?.code, { month: "short" }),
12
22
  ...formatters,
@@ -33,11 +43,13 @@ function Calendar({ className, classNames, showOutsideDays = true, captionLayout
33
43
  months: cn("relative flex flex-col gap-4 md:flex-row", defaultClassNames.months),
34
44
  month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
35
45
  nav: cn("absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1", defaultClassNames.nav),
36
- button_previous: cn(buttonVariants({ variant: buttonVariant }), "size-(--cell-size) p-0 select-none aria-disabled:opacity-50", defaultClassNames.button_previous),
37
- button_next: cn(buttonVariants({ variant: buttonVariant }), "size-(--cell-size) p-0 select-none aria-disabled:opacity-50", defaultClassNames.button_next),
46
+ button_previous: cn(navButton, defaultClassNames.button_previous),
47
+ button_next: cn(navButton, defaultClassNames.button_next),
38
48
  month_caption: cn("flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)", defaultClassNames.month_caption),
39
49
  dropdowns: cn("flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium", defaultClassNames.dropdowns),
40
- dropdown_root: cn("relative rounded-(--cell-radius)", defaultClassNames.dropdown_root),
50
+ // The native select is invisible over the caption, so its wrapper
51
+ // shows keyboard focus with the kit's solid ring.
52
+ dropdown_root: cn("relative rounded-(--cell-radius) has-focus-visible:ring-2 has-focus-visible:ring-ring", defaultClassNames.dropdown_root),
41
53
  dropdown: cn("absolute inset-0 bg-popover opacity-0", defaultClassNames.dropdown),
42
54
  caption_label: cn("font-medium select-none", captionLayout === "label"
43
55
  ? "text-sm"
@@ -63,14 +75,9 @@ function Calendar({ className, classNames, showOutsideDays = true, captionLayout
63
75
  Root: ({ className, rootRef, ...props }) => {
64
76
  return (_jsx("div", { "data-slot": "calendar", ref: rootRef, className: cn(className), ...props }));
65
77
  },
66
- Chevron: ({ className, orientation, ...props }) => {
67
- if (orientation === "left") {
68
- return (_jsx(ChevronLeftIcon, { className: cn("size-4", className), ...props }));
69
- }
70
- if (orientation === "right") {
71
- return (_jsx(ChevronRightIcon, { className: cn("size-4", className), ...props }));
72
- }
73
- return (_jsx(ChevronDownIcon, { className: cn("size-4", className), ...props }));
78
+ Chevron: ({ className, orientation = "down", ...props }) => {
79
+ const Icon = chevronIcons[orientation];
80
+ return _jsx(Icon, { className: cn("size-4", className), ...props });
74
81
  },
75
82
  DayButton: ({ ...props }) => (_jsx(CalendarDayButton, { locale: locale, ...props })),
76
83
  WeekNumber: ({ children, ...props }) => {
package/dist/card.d.ts CHANGED
@@ -13,7 +13,7 @@ declare function Card({ className, size, flush, ...props }: React.ComponentProps
13
13
  }): React.JSX.Element;
14
14
  declare function CardHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
15
15
  declare function CardTitle({ as: Component, className, ...props }: React.ComponentProps<"div"> & {
16
- as?: "div" | "h1";
16
+ as?: "div" | "h1" | "h2" | "h3" | "h4";
17
17
  }): React.JSX.Element;
18
18
  declare function CardDescription({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
19
19
  declare function CardAction({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
package/dist/checkbox.js CHANGED
@@ -2,10 +2,10 @@
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { Checkbox as CheckboxPrimitive } from "radix-ui";
5
- import { disabledState, focusRing, invalidState } from "./internal/styles.js";
5
+ import { choiceHitArea, disabledState, focusRing, invalidState, } from "./internal/styles.js";
6
6
  import { cn } from "./utils.js";
7
7
  import { CheckIcon } from "lucide-react";
8
8
  function Checkbox({ className, ...props }) {
9
- return (_jsx(CheckboxPrimitive.Root, { "data-slot": "checkbox", className: cn(focusRing, invalidState, disabledState, "peer relative flex size-4.5 shrink-0 items-center justify-center rounded-none border border-input bg-transparent transition-shadow group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 aria-invalid:aria-checked:border-primary data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary", className), ...props, children: _jsx(CheckboxPrimitive.Indicator, { "data-slot": "checkbox-indicator", className: "grid place-content-center text-current transition-none [&>svg]:size-3.5", children: _jsx(CheckIcon, {}) }) }));
9
+ return (_jsx(CheckboxPrimitive.Root, { "data-slot": "checkbox", className: cn(focusRing, invalidState, disabledState, choiceHitArea, "peer relative flex size-4.5 shrink-0 items-center justify-center rounded-none border border-input bg-transparent transition-shadow group-has-disabled/field:opacity-50 aria-invalid:aria-checked:border-primary data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary", className), ...props, children: _jsx(CheckboxPrimitive.Indicator, { "data-slot": "checkbox-indicator", className: "grid place-content-center text-current transition-none [&>svg]:size-3.5", children: _jsx(CheckIcon, {}) }) }));
10
10
  }
11
11
  export { Checkbox };
@@ -5,7 +5,7 @@ export type ColorLegendItem = {
5
5
  range?: string;
6
6
  label: string;
7
7
  };
8
- export type ColorLegendProps = {
8
+ export type ColorLegendProps = Omit<React.ComponentProps<"figure">, "children" | "title"> & {
9
9
  title?: string;
10
10
  items: ColorLegendItem[];
11
11
  caption?: React.ReactNode;
@@ -15,8 +15,10 @@ export type ColorLegendProps = {
15
15
  };
16
16
  /**
17
17
  * Key for a color scale: swatch, optional range, and label. Overlay chrome
18
- * (absolute position, card fill) is the caller's `className`. Attribution
19
- * and license lines stay in the app.
18
+ * (absolute position, card fill) is the caller's `className`; other props go
19
+ * to the `figure`. Parts carry `data-slot="color-legend-*"` (title, list,
20
+ * item, swatch, range, label, caption). Attribution and license lines stay
21
+ * in the app.
20
22
  */
21
- declare function ColorLegend({ title, items, caption, children, "aria-label": ariaLabel, className, }: ColorLegendProps): React.JSX.Element;
23
+ declare function ColorLegend({ title, items, caption, children, className, ...props }: ColorLegendProps): React.JSX.Element;
22
24
  export { ColorLegend };
@@ -1,11 +1,14 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { sectionLabel } from "./internal/styles.js";
2
3
  import { cn } from "./utils.js";
3
4
  /**
4
5
  * Key for a color scale: swatch, optional range, and label. Overlay chrome
5
- * (absolute position, card fill) is the caller's `className`. Attribution
6
- * and license lines stay in the app.
6
+ * (absolute position, card fill) is the caller's `className`; other props go
7
+ * to the `figure`. Parts carry `data-slot="color-legend-*"` (title, list,
8
+ * item, swatch, range, label, caption). Attribution and license lines stay
9
+ * in the app.
7
10
  */
8
- function ColorLegend({ title, items, caption, children, "aria-label": ariaLabel, className, }) {
9
- return (_jsxs("figure", { "data-slot": "color-legend", "aria-label": ariaLabel, className: cn("flex flex-col gap-2 text-sm", className), children: [title ? (_jsx("figcaption", { className: "font-heading text-xs font-semibold tracking-widest text-muted-foreground uppercase", children: title })) : null, _jsx("ul", { className: "flex flex-col gap-1.5", children: items.map((item) => (_jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { "aria-hidden": true, className: "size-3.5 shrink-0 border border-input", style: { backgroundColor: item.color } }), item.range ? (_jsx("span", { className: "w-16 shrink-0 tabular-nums text-muted-foreground", children: item.range })) : null, _jsx("span", { children: item.label })] }, `${item.color}-${item.range ?? ""}-${item.label}`))) }), caption ? (_jsx("p", { className: "text-xs text-muted-foreground", children: caption })) : null, children] }));
11
+ function ColorLegend({ title, items, caption, children, className, ...props }) {
12
+ return (_jsxs("figure", { "data-slot": "color-legend", className: cn("flex flex-col gap-2 text-sm", className), ...props, children: [title ? (_jsx("figcaption", { "data-slot": "color-legend-title", className: cn("font-heading", sectionLabel), children: title })) : null, _jsx("ul", { "data-slot": "color-legend-list", className: "flex flex-col gap-1.5", children: items.map((item) => (_jsxs("li", { "data-slot": "color-legend-item", className: "flex items-center gap-2", children: [_jsx("span", { "aria-hidden": true, "data-slot": "color-legend-swatch", className: "size-3.5 shrink-0 border border-input", style: { backgroundColor: item.color } }), item.range ? (_jsx("span", { "data-slot": "color-legend-range", className: "w-16 shrink-0 tabular-nums text-muted-foreground", children: item.range })) : null, _jsx("span", { "data-slot": "color-legend-label", children: item.label })] }, `${item.color}-${item.range ?? ""}-${item.label}`))) }), caption ? (_jsx("p", { "data-slot": "color-legend-caption", className: "text-xs text-muted-foreground", children: caption })) : null, children] }));
10
13
  }
11
14
  export { ColorLegend };
@@ -16,6 +16,11 @@ type ComboboxProps = {
16
16
  className?: string;
17
17
  /** Classes for the listbox, e.g. its height. Defaults to `max-h-60`. */
18
18
  listClassName?: string;
19
+ /**
20
+ * Classes for the popover panel, e.g. a width other than the trigger's.
21
+ * Defaults to the trigger width.
22
+ */
23
+ contentClassName?: string;
19
24
  "aria-invalid"?: boolean;
20
25
  /** Name the trigger when there is no `<Label htmlFor={id}>`. */
21
26
  "aria-label"?: string;
@@ -36,7 +41,7 @@ type ComboboxProps = {
36
41
  * `Create "{value}"`.
37
42
  */
38
43
  createLabel?: (value: string) => string;
39
- };
44
+ } & Omit<React.ComponentProps<"button">, "value" | "defaultValue" | "children" | "type" | "role" | "aria-expanded" | "aria-controls" | "onKeyDown">;
40
45
  /**
41
46
  * Searchable single-select built from Button, Popover, and Input.
42
47
  * The trigger uses the same form-control chrome as Input and SelectTrigger.
@@ -46,5 +51,5 @@ type ComboboxProps = {
46
51
  * Opt-in `creatable` appends a create action when the query is not already
47
52
  * an option; choosing it calls `onValueChange` like a normal selection.
48
53
  */
49
- export declare function Combobox({ options, value, onValueChange, placeholder, searchPlaceholder, emptyText, disabled, id, className, listClassName, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, creatable, normalizeCreateValue, createLabel, }: ComboboxProps): React.JSX.Element;
54
+ export declare function Combobox({ options, value, onValueChange, placeholder, searchPlaceholder, emptyText, disabled, id, className, listClassName, contentClassName, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, creatable, normalizeCreateValue, createLabel, ...props }: ComboboxProps): React.JSX.Element;
50
55
  export {};