@uxelle/skills 0.2.0-beta.2

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 (117) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +58 -0
  3. package/dist/index.d.ts +26 -0
  4. package/dist/index.js +586 -0
  5. package/index.json +573 -0
  6. package/package.json +57 -0
  7. package/skills/uxelle-components/Accordion.md +51 -0
  8. package/skills/uxelle-components/AccordionGroup.md +39 -0
  9. package/skills/uxelle-components/Banner.md +73 -0
  10. package/skills/uxelle-components/BannerAnnouncement.md +63 -0
  11. package/skills/uxelle-components/Breadcrumb.md +38 -0
  12. package/skills/uxelle-components/Breadcrumbs.md +32 -0
  13. package/skills/uxelle-components/Bubble.md +34 -0
  14. package/skills/uxelle-components/Button.md +54 -0
  15. package/skills/uxelle-components/ButtonGroup.md +43 -0
  16. package/skills/uxelle-components/Card.md +35 -0
  17. package/skills/uxelle-components/Checkbox.md +57 -0
  18. package/skills/uxelle-components/CheckboxGroup.md +47 -0
  19. package/skills/uxelle-components/ChoiceChip.md +44 -0
  20. package/skills/uxelle-components/ChoiceChipGroup.md +50 -0
  21. package/skills/uxelle-components/DatePicker.md +59 -0
  22. package/skills/uxelle-components/DatePickerRange.md +73 -0
  23. package/skills/uxelle-components/Dialog.md +83 -0
  24. package/skills/uxelle-components/Divider.md +37 -0
  25. package/skills/uxelle-components/DynamicAngleLine.md +36 -0
  26. package/skills/uxelle-components/DynamicAngleLineCombo.md +30 -0
  27. package/skills/uxelle-components/DynamicAngleShape.md +32 -0
  28. package/skills/uxelle-components/DynamicAngleShapeSurface.md +35 -0
  29. package/skills/uxelle-components/DynamicAngleTexture.md +29 -0
  30. package/skills/uxelle-components/DynamicAngleTextureText.md +35 -0
  31. package/skills/uxelle-components/FieldMessage.md +31 -0
  32. package/skills/uxelle-components/FileUpload.md +53 -0
  33. package/skills/uxelle-components/FilterChip.md +35 -0
  34. package/skills/uxelle-components/FilterChipGroup.md +42 -0
  35. package/skills/uxelle-components/Footer.md +34 -0
  36. package/skills/uxelle-components/Hero.md +61 -0
  37. package/skills/uxelle-components/Icon.md +43 -0
  38. package/skills/uxelle-components/IconButton.md +44 -0
  39. package/skills/uxelle-components/Image.md +40 -0
  40. package/skills/uxelle-components/Label.md +35 -0
  41. package/skills/uxelle-components/LabelBadge.md +58 -0
  42. package/skills/uxelle-components/LanguageSelector.md +38 -0
  43. package/skills/uxelle-components/LanguageSelectorButton.md +34 -0
  44. package/skills/uxelle-components/Layout.md +81 -0
  45. package/skills/uxelle-components/LinearProgress.md +53 -0
  46. package/skills/uxelle-components/Link.md +52 -0
  47. package/skills/uxelle-components/List.md +35 -0
  48. package/skills/uxelle-components/ListControls.md +100 -0
  49. package/skills/uxelle-components/ListItem.md +69 -0
  50. package/skills/uxelle-components/Lockup.md +50 -0
  51. package/skills/uxelle-components/Logo.md +39 -0
  52. package/skills/uxelle-components/Menu.md +48 -0
  53. package/skills/uxelle-components/MultiSelect.md +54 -0
  54. package/skills/uxelle-components/NavLink.md +44 -0
  55. package/skills/uxelle-components/Navigation.md +61 -0
  56. package/skills/uxelle-components/NavigationSide.md +51 -0
  57. package/skills/uxelle-components/NavigationSideGroup.md +36 -0
  58. package/skills/uxelle-components/NavigationSideItem.md +56 -0
  59. package/skills/uxelle-components/NavigationSideSubItem.md +39 -0
  60. package/skills/uxelle-components/NotificationBadge.md +62 -0
  61. package/skills/uxelle-components/Pagination.md +49 -0
  62. package/skills/uxelle-components/PaginationComplex.md +51 -0
  63. package/skills/uxelle-components/PaginationSimple.md +44 -0
  64. package/skills/uxelle-components/ProductCard.md +46 -0
  65. package/skills/uxelle-components/Quantity.md +45 -0
  66. package/skills/uxelle-components/Radio.md +55 -0
  67. package/skills/uxelle-components/RadioGroup.md +49 -0
  68. package/skills/uxelle-components/SKILL.md +242 -0
  69. package/skills/uxelle-components/Scrim.md +28 -0
  70. package/skills/uxelle-components/SegmentedControl.md +118 -0
  71. package/skills/uxelle-components/Select.md +55 -0
  72. package/skills/uxelle-components/Sheet.md +31 -0
  73. package/skills/uxelle-components/Skeleton.md +35 -0
  74. package/skills/uxelle-components/Spinner.md +33 -0
  75. package/skills/uxelle-components/StatTile.md +150 -0
  76. package/skills/uxelle-components/Stepper.md +73 -0
  77. package/skills/uxelle-components/Switch.md +41 -0
  78. package/skills/uxelle-components/Tab.md +41 -0
  79. package/skills/uxelle-components/Table.md +55 -0
  80. package/skills/uxelle-components/TabsCompact.md +37 -0
  81. package/skills/uxelle-components/TabsStretch.md +35 -0
  82. package/skills/uxelle-components/Text.md +56 -0
  83. package/skills/uxelle-components/Textarea.md +77 -0
  84. package/skills/uxelle-components/Textfield.md +62 -0
  85. package/skills/uxelle-components/Tooltip.md +33 -0
  86. package/skills/uxelle-design-harness/SKILL.md +149 -0
  87. package/skills/uxelle-design-harness/a2ui.md +73 -0
  88. package/skills/uxelle-design-harness/how-to-accessibility.md +31 -0
  89. package/skills/uxelle-design-harness/how-to-color.md +82 -0
  90. package/skills/uxelle-design-harness/how-to-host.md +108 -0
  91. package/skills/uxelle-design-harness/how-to-page-layout.md +203 -0
  92. package/skills/uxelle-design-harness/principles.md +72 -0
  93. package/skills/uxelle-design-harness/recipe-app-chrome.md +168 -0
  94. package/skills/uxelle-design-harness/recipe-card-grid.md +104 -0
  95. package/skills/uxelle-design-harness/recipe-cta-band.md +91 -0
  96. package/skills/uxelle-design-harness/recipe-dashboard-overview.md +118 -0
  97. package/skills/uxelle-design-harness/recipe-data-table-page.md +323 -0
  98. package/skills/uxelle-design-harness/recipe-feature-section.md +96 -0
  99. package/skills/uxelle-design-harness/recipe-footer.md +156 -0
  100. package/skills/uxelle-design-harness/recipe-form-section.md +163 -0
  101. package/skills/uxelle-design-harness/recipe-hero.md +89 -0
  102. package/skills/uxelle-design-harness/recipe-landing-page.md +94 -0
  103. package/skills/uxelle-design-harness/recipe-logo-wall.md +80 -0
  104. package/skills/uxelle-design-harness/recipe-multi-step-flow.md +108 -0
  105. package/skills/uxelle-design-harness/recipe-page-header.md +102 -0
  106. package/skills/uxelle-design-harness/recipe-page-shell.md +82 -0
  107. package/skills/uxelle-design-harness/recipe-pricing.md +83 -0
  108. package/skills/uxelle-design-harness/recipe-query-bar.md +108 -0
  109. package/skills/uxelle-design-harness/recipe-record-detail.md +96 -0
  110. package/skills/uxelle-design-harness/recipe-settings-page.md +170 -0
  111. package/skills/uxelle-design-harness/recipe-stat-callouts.md +111 -0
  112. package/skills/uxelle-design-harness/recipe-states.md +119 -0
  113. package/skills/uxelle-design-harness/recipe-summary-list.md +88 -0
  114. package/skills/uxelle-design-harness/recipe-template.md +62 -0
  115. package/skills/uxelle-design-harness/recipe-testimonial.md +73 -0
  116. package/skills/uxelle-design-harness/spacing-steps.md +57 -0
  117. package/skills/uxelle-design-harness/tokens.md +101 -0
@@ -0,0 +1,39 @@
1
+ # AccordionGroup
2
+
3
+ Import: `import { AccordionGroup } from "@uxelle/components";`
4
+
5
+ Vertical stack of `Accordion` sections spaced by theme gap.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | children | `ReactNode` | `—` | `Accordion` sections stacked in a vertical column with theme gap between them. Each child keeps its own expanded state — the group does not enforce one-open-at-a-time. |
14
+
15
+ <!-- prettier-ignore-end -->
16
+
17
+ Extends `HTMLAttributes` — supports standard HTML attributes.
18
+
19
+ ## Usage
20
+
21
+ ```tsx
22
+ <AccordionGroup>
23
+ <Accordion>
24
+ <Text type="Body Medium Alt">Section 1</Text>
25
+ <Accordion.Panel>
26
+ <Text type="Body Small">Content...</Text>
27
+ </Accordion.Panel>
28
+ </Accordion>
29
+ <Accordion>
30
+ <Text type="Body Medium Alt">Section 2</Text>
31
+ </Accordion>
32
+ </AccordionGroup>
33
+ ```
34
+
35
+
36
+ ## Notes
37
+
38
+ - **Slot**: Accepts Accordion children
39
+ - **Layout**: Vertical stack with gap between elevated accordion cards
@@ -0,0 +1,73 @@
1
+ # Banner
2
+
3
+ Import: `import { Banner } from "@uxelle/components";`
4
+
5
+ In-page messaging for contextual updates—section notices, task feedback, and supplemental copy with optional actions.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | alert | `boolean` | `false` | Marks the message as urgent for assistive technology: the root becomes role="alert" with aria-live="assertive" so it is announced as soon as it appears, unless you override `role` or `aria-live`. Nothing changes visually. |
14
+ | open | `boolean` | `true` | Whether the banner is present. Setting it false plays the leave animation and then removes the banner from layout, so keep the component mounted and toggle this instead of conditionally rendering it. |
15
+ | inline | `boolean` | `true` | Layout of copy and actions: true puts them on one row inside a max-width content region, false stacks actions under the copy and anchors trailing content to the top-right. |
16
+ | dismissible | `boolean` | `true` | Adds the close control and an elevated surface, suited to a banner floating under navigation; false renders a flat banner in document flow. Dismiss only calls `onDismiss`, so set `open` to false there to animate the banner away. |
17
+ | trailingSlot | `boolean` | `false` | Renders the trailing region holding `trailing` — after the actions when `inline` is true, at the top-right otherwise. Ignored while `dismissible` is true, because the close control owns the trailing edge. |
18
+ | trailing | `ReactNode` | `—` | Content for the trailing region, such as a **Link** or a timestamp. Rendered only when `trailingSlot` is true and `dismissible` is false. |
19
+ | children | `ReactNode` | `—` | Message copy for the banner. A string is wrapped in `Text` with `type=\"Body Medium Alt\"`; pass composed `Text` nodes for a title plus supporting copy. Omitting it renders an empty copy region — there is no placeholder. |
20
+ | onDismiss | `() => void` | `—` | Fires when the close control is activated. The banner never hides itself, so set `open` to false here to start the leave animation. |
21
+ | onExitComplete | `() => void` | `—` | Fires once the leave animation has finished and the banner is out of the layout. Use it to move keyboard focus somewhere sensible after a dismissal. |
22
+ | dismissLabel | `string` | `"Dismiss"` | Accessible name for the close `IconButton`, which has no visible text. Localize it for non-English UIs. |
23
+ | dataColorSwitcher | `string` | `"info"` | Status palette for the banner surface, text, and icon colors — for example `info`, `success`, `warning`, or `danger`. Sets `data-color-switcher` on the root. |
24
+ | "data-color-switcher" | `string` | `—` | Attribute spelling of `dataColorSwitcher`, for call sites that spread DOM props. Wins over `dataColorSwitcher` when both are set. |
25
+ | leadingSlot | `boolean` | `true` | Reserves the leading region and renders `leadingSlotContent`, which becomes required. Set false to drop the region entirely. |
26
+ | leadingSlotContent | `ReactNode` | `null` | Content for the leading region, typically a decorative `Icon` marked `aria-hidden`. Required when `leadingSlot` is true and ignored when it is false. |
27
+ | actionsSlot | `boolean` | `true` | Renders the actions region holding `actionsSlotContent`, which becomes required. Set false when the banner is copy-only. |
28
+ | actionsSlotContent | `ReactNode` | `null` | Content for the actions region, typically a `ButtonGroup` of `Button` children. Required when `actionsSlot` is true and ignored when it is false. |
29
+ | "aria-label" | `string` | `""` | Names the banner's region, which turns it into a landmark; leave it unset for ordinary in-page notices so the copy is not announced twice. Ignored when `aria-labelledby` is set. |
30
+ | "aria-labelledby" | `string` | `""` | Space-separated ids of elements that name the region and turn it into a landmark. Takes precedence over `aria-label`. |
31
+ | "aria-live" | `"polite" \| "assertive" \| "off"` | `""` | How the banner is announced while it is present. Leave it unset for static copy and use `polite` when a non-alert banner appears in response to something the user did; setting `alert` already implies `assertive`. |
32
+
33
+ <!-- prettier-ignore-end -->
34
+
35
+ ## Usage
36
+
37
+ ```tsx
38
+ const [open, setOpen] = useState(true);
39
+
40
+ return (
41
+ <Banner
42
+ open={open}
43
+ leadingSlotContent={
44
+ <Icon iconName="info" size={24} variant="sharpUnfilled" aria-hidden />
45
+ }
46
+ actionsSlotContent={
47
+ <ButtonGroup direction="row">
48
+ <Button emphasis="high" size="medium">
49
+ <Text type="Button" width>Continue</Text>
50
+ </Button>
51
+ </ButtonGroup>
52
+ }
53
+ onDismiss={() => setOpen(false)}
54
+ dismissLabel="Dismiss notice"
55
+ >
56
+ <Text type="Body Medium Alt" width>Schedule updated.</Text>
57
+ </Banner>
58
+ );
59
+ ```
60
+
61
+
62
+ ## Notes
63
+
64
+ - Usually sits within the content area when `dismissible` is false (flat, in-flow). When `dismissible` is true, elevation supports placement under navigation floating above content without pushing layout down. Prefer `BannerAnnouncement` when a system-wide bar should displace the page by its height in document flow.
65
+ - **Presence**: Controlled with `open`. Leave animates then unmounts from layout (respects `prefers-reduced-motion`). Keep mounted and toggle `open` for exit motion.
66
+ - **Copy**: Pass `children` (string or composed `Text`). Empty children render an empty copy region — there is no built-in placeholder copy.
67
+ - **Leading**: Pass `leadingSlotContent` when `leadingSlot` is true (typically an `Icon`).
68
+ - **Actions**: Pass `actionsSlotContent` when `actionsSlot` is true (typically `ButtonGroup` with `Button` children). Set `actionsSlot={false}` to hide the region.
69
+ - **Layout**: Use `inline` for a single-row max-width layout; turn it off to stack copy above actions with trailing content top-right.
70
+ - **Trailing**: `trailingSlot` is ignored when `dismissible` is true; dismiss owns the trailing edge. Pass `trailing` only when `trailingSlot` is true and not dismissible.
71
+ - **Dismissible**: When true, shows the close control and elevated surface. Set `open={false}` in `onDismiss`; use `onExitComplete` to restore focus after leave.
72
+ - **Alert**: Accessibility only. When `alert` is true, uses `role="alert"` and `aria-live="assertive"`. Otherwise the banner is a `region` with no `aria-live`, so static in-page copy is not announced as a live region. Pass `aria-live="polite"` when a non-alert banner is shown dynamically. Override `role` / `aria-live` as needed.
73
+ - **Accessibility**: The `region` is unnamed by default, so an in-page banner is not exposed as a landmark and its copy is announced once (never auto-named from copy). Pass `aria-label` / `aria-labelledby` only when the banner should be a named landmark. Alerts announce their message when they appear and are likewise never named from copy. Localize `dismissLabel` for non-English UIs.
@@ -0,0 +1,63 @@
1
+ # BannerAnnouncement
2
+
3
+ Import: `import { BannerAnnouncement } from "@uxelle/components";`
4
+
5
+ Compact system announcement bar for product-wide messaging above navigation.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | alert | `boolean` | `false` | Marks the announcement as urgent for assistive technology: the root becomes role="alert" with aria-live="assertive" so it is announced as soon as it appears, unless you override `role` or `aria-live`. Nothing changes visually. |
14
+ | open | `boolean` | `true` | Whether the bar is present. Setting it false animates height and opacity closed and then removes the bar from layout, reflowing the page, so keep the component mounted and toggle this instead of conditionally rendering it. |
15
+ | dismissible | `boolean` | `true` | Shows the close control at the far edge of the bar. It only calls `onDismiss`, so set `open` to false there to animate the bar away; a missing `onDismiss` warns in development. |
16
+ | trailingSlot | `boolean` | `true` | Renders the trailing region after the message inside the centered content row. Independent of `dismissible`, whose close control is positioned separately at the far edge. |
17
+ | trailing | `ReactNode` | `null` | Content for the trailing region, typically a `Link` that continues the announcement. Rendered only when `trailingSlot` is true. |
18
+ | children | `ReactNode` | `—` | Announcement message; a string is wrapped in `Text` with `type=\"Body Medium Alt\"`. The rendered message also names the landmark region unless you pass `aria-label` or `aria-labelledby`, and leaving it empty warns in development. |
19
+ | onDismiss | `() => void` | `—` | Fires when the close control is activated. The bar never hides itself, so set `open` to false here to start the leave animation. |
20
+ | onExitComplete | `() => void` | `—` | Fires once the leave animation has finished and the bar is out of the layout. Use it to move keyboard focus somewhere sensible, such as the main landmark. |
21
+ | dismissLabel | `string` | `"Dismiss"` | Accessible name for the close `IconButton`, which has no visible text. Localize it for non-English UIs. |
22
+ | dataColorSwitcher | `string` | `"success"` | Status palette for the bar surface, text, and icon colors — for example `success`, `info`, `warning`, or `danger`. Sets `data-color-switcher` on the root. |
23
+ | "data-color-switcher" | `string` | `—` | Attribute spelling of `dataColorSwitcher`, for call sites that spread DOM props. Wins over `dataColorSwitcher` when both are set. |
24
+ | leadingSlot | `boolean` | `true` | Reserves the leading region and renders `leadingSlotContent`, which becomes required. Set false to drop the region entirely. |
25
+ | leadingSlotContent | `ReactNode` | `null` | Content for the leading region, typically a decorative `Icon` marked `aria-hidden`. Required when `leadingSlot` is true and ignored when it is false. |
26
+ | "aria-label" | `string` | `""` | Names the bar's landmark region, overriding the name generated from the message copy. Usually unnecessary, and ignored when `aria-labelledby` is set. |
27
+ | "aria-labelledby" | `string` | `""` | Space-separated ids of elements that name the landmark region. Takes precedence over both `aria-label` and the name generated from the message copy. |
28
+ | "aria-live" | `"polite" \| "assertive" \| "off"` | `"off"` | How the bar is announced while it is present. It stays `off` for a bar rendered with the page; use `polite`, or set `alert` for `assertive`, when the bar mounts later in response to an event. |
29
+
30
+ <!-- prettier-ignore-end -->
31
+
32
+ ## Usage
33
+
34
+ ```tsx
35
+ const [open, setOpen] = useState(true);
36
+ const mainRef = useRef<HTMLElement>(null);
37
+
38
+ return (
39
+ <BannerAnnouncement
40
+ open={open}
41
+ leadingSlotContent={<Icon iconName="campaign" size={24} variant="sharpUnfilled" aria-hidden />}
42
+ trailing={<Link href="/whats-new">Take a look</Link>}
43
+ onDismiss={() => setOpen(false)}
44
+ onExitComplete={() => mainRef.current?.focus()}
45
+ dismissLabel="Dismiss announcement"
46
+ aria-live="polite"
47
+ >
48
+ Something new is here.
49
+ </BannerAnnouncement>
50
+ );
51
+ ```
52
+
53
+
54
+ ## Notes
55
+
56
+ - Sits above navigation and pushes page content down by its height. Prefer `Banner` for in-page contextual notices that should not displace the whole layout.
57
+ - **Placement**: Above navigation; not a page heading — do not substitute for `h1`.
58
+ - **Presence**: Controlled with `open`. Leave animates height/opacity then unmounts from layout (respects `prefers-reduced-motion`). Keep the component in the tree and toggle `open` — conditional render skips exit motion.
59
+ - **Leading**: Pass `leadingSlotContent` when `leadingSlot` is true (typically an `Icon`).
60
+ - **Copy**: Pass a string, `Text`, or composed message as `children`. Message copy names the landmark automatically unless you pass `aria-label` or `aria-labelledby`.
61
+ - **Trailing**: Pass `trailing` when `trailingSlot` is true (typically a `Link`).
62
+ - **Dismiss**: Calls `onDismiss` only — set `open={false}` there. Use `onExitComplete` to restore focus after leave.
63
+ - **Accessibility**: Defaults to `role="region"` named from the message. Defaults to `aria-live="off"`; set `alert` (or `aria-live`) when mounting dynamically after load.
@@ -0,0 +1,38 @@
1
+ # Breadcrumb
2
+
3
+ Import: `import { Breadcrumb } from "@uxelle/components";`
4
+
5
+ Single breadcrumb item. Compose multiple instances inside `Breadcrumbs` for a full trail. Can also render standalone as a list item when building custom layouts.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | label | `string` | `"Page"` | Crumb text for link and current-page crumbs. Required unless `collapse` is true — a blank label skips the item and logs a development warning. |
14
+ | href | `string` | `""` | Destination URL for an ancestor crumb, rendered through `Link`. Omit it and the crumb falls back to current-page text. Ignored when `currentPage` or `collapse` is true. |
15
+ | currentPage | `boolean` | `false` | Renders the crumb as non-interactive bold text instead of a link. Inside `Breadcrumbs` this is set automatically on the last child. |
16
+ | collapse | `boolean` | `false` | Replaces the crumb with the overflow menu control that lists `collapseItems`. `Breadcrumbs` inserts this crumb itself when a trail collapses, so set it only when hand-building a trail. |
17
+ | collapseItems | `BreadcrumbCollapseItem[]` | `—` | Ancestor crumbs listed in the overflow menu when `collapse` is true, each `{ label, href? }`. Entries without `href` render as non-interactive rows. |
18
+ | expandLabel | `string` | `"Show hidden breadcrumbs"` | Accessible name for the icon-only overflow menu trigger, which has no visible text. Localize for your locale; you may include the hidden crumb count. |
19
+ | chevron | `boolean` | `true` | Renders the leading chevron separator before this crumb. `Breadcrumbs` manages it — `false` on the first visible crumb, `true` on the rest. |
20
+
21
+ <!-- prettier-ignore-end -->
22
+
23
+ Extends `React.LiHTMLAttributes` — supports standard HTML attributes.
24
+
25
+ ## Usage
26
+
27
+ ```tsx
28
+ <Breadcrumb>
29
+ {/* Content */}
30
+ </Breadcrumb>
31
+ ```
32
+
33
+ ## Notes
34
+
35
+ - Compose inside `Breadcrumbs` for a full trail with the correct `nav` landmark. Ancestors use `Link`; the current page is plain text.
36
+ - With `href` (and not `currentPage` / `collapse`): interactive link.
37
+ - With `currentPage`: non-interactive bold text.
38
+ - With `collapse`: overflow menu for hidden ancestor crumbs.
@@ -0,0 +1,32 @@
1
+ # Breadcrumbs
2
+
3
+ Import: `import { Breadcrumbs } from "@uxelle/components";`
4
+
5
+ Compose `Breadcrumb` children for the route.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | children (required) | `ReactNode` | `—` | `Breadcrumb` children in route order, root first, rendered into an `<ol>`. The last one is always shown as the current page. Other element types are dropped with a development warning. |
14
+ | overflow | `boolean` | `true` | Collapses middle crumbs into an overflow menu once the trail is longer than the root plus its tail (the last crumb for trails of three or fewer, the last two above that). Set `false` to keep every crumb visible. |
15
+ | expandLabel | `string` | `"Show hidden breadcrumbs"` | Accessible name for the icon-only overflow menu trigger, which has no visible text. Localize for your locale; you may include the hidden crumb count. |
16
+ | "aria-label" | `string` | `"Breadcrumb"` | Accessible name for the `<nav>` landmark wrapping the trail. Localize for your locale (e.g. `Fil d'Ariane`). |
17
+
18
+ <!-- prettier-ignore-end -->
19
+
20
+ Extends `React.HTMLAttributes` — supports standard HTML attributes.
21
+
22
+ ## Usage
23
+
24
+ ```tsx
25
+ <Breadcrumbs>
26
+ {/* React.ReactNode */}
27
+ </Breadcrumbs>
28
+ ```
29
+
30
+ ## Notes
31
+
32
+ - Pass `href` for ancestors; the last child is always treated as the current page. Ancestor links use the shared `Link` component. When `overflow` is enabled, the bar shows **Home**, the overflow trigger, the **parent level**, and **Current Page**; ancestors between them are listed in `collapseItems`. Middle segments collapse into an overflow menu after the root crumb.
@@ -0,0 +1,34 @@
1
+ # Bubble
2
+
3
+ Import: `import { Bubble } from "@uxelle/components";`
4
+
5
+ Non-interactive circular marker. Nest Text as children, or set `icon` with `iconName` for a glyph.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | icon | `boolean` | `false` | Switches the bubble to a single glyph from `iconName` and ignores `children`. When false, `children` are rendered instead and development builds warn if there are none. |
14
+ | iconName | `string` | `"add"` | Material Symbol glyph shown while `icon` is true. It is `aria-hidden`, so convey the meaning through `aria-label` or nearby copy. |
15
+ | iconVariant | `"sharpUnfilled" \| "sharpFilled"` | `"sharpUnfilled"` | Fill style for the glyph while `icon` is true: `sharpFilled` solid, `sharpUnfilled` outlined. |
16
+ | children | `ReactNode` | `—` | Content shown while `icon` is false — typically one or two initials in `Text` (`<Text type=\"Body Small Alt\" width inline>Aa</Text>`). Nothing renders when this is empty. |
17
+ | "aria-label" | `string` | `""` | Accessible name for the bubble, needed when it is the only indicator of a category and its initials or glyph do not convey that on their own. |
18
+ | "aria-hidden" | `"true" \| "false"` | `""` | Hides the bubble from assistive technology. Set it when the marker is purely decorative or repeats copy that sits next to it. |
19
+
20
+ <!-- prettier-ignore-end -->
21
+
22
+ Extends `React.HTMLAttributes` — supports standard HTML attributes.
23
+
24
+ ## Usage
25
+
26
+ ```tsx
27
+ <Bubble>
28
+ {/* Content */}
29
+ </Bubble>
30
+ ```
31
+
32
+ ## Notes
33
+
34
+ - Use for decoration or category—initials, type glyphs, or a single icon. Not a button, link, or actionable control. Do not use as a button or link. When decorative, set `aria-hidden={true}`; when the sole category indicator, expose meaning on a parent or via `aria-label`.
@@ -0,0 +1,54 @@
1
+ # Button
2
+
3
+ Import: `import { Button } from "@uxelle/components";`
4
+
5
+ Standard action control for forms, dialogs, and toolbars. Label with `children`; Use `IconButton` for icon-only actions.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | emphasis | `"high" \| "medium" \| "low"` | `"high"` | Visual weight: `high` is a filled surface for the one primary action in a view, `medium` an outlined surface for secondary actions, `low` text-only for tertiary or inline actions. |
14
+ | size | `"extraSmall" \| "small" \| "medium" \| "large"` | `"medium"` | Padding and gap scale — the label `Text` type and icon size stay fixed. Use `extraSmall` for dense toolbars (it matches the `IconButton` box height) and `large` for prominent page-level actions. |
15
+ | leadingIcon | `boolean` | `false` | Renders `leadingIconName` before the label. The icon is `aria-hidden`, so its meaning must also live in `children` or `aria-label`. |
16
+ | trailingIcon | `boolean` | `false` | Renders `trailingIconName` after the label. The icon is `aria-hidden`, so its meaning must also live in `children` or `aria-label`. |
17
+ | leadingIconName | `string` | `"chevron_forward"` | Material Symbol glyph used by `leadingIcon`; ignored while `leadingIcon` is false. |
18
+ | trailingIconName | `string` | `"chevron_forward"` | Material Symbol glyph used by `trailingIcon`; ignored while `trailingIcon` is false. |
19
+ | leadingIconVariant | `"sharpUnfilled" \| "sharpFilled"` | `"sharpUnfilled"` | Fill style for the leading icon: `sharpFilled` solid, `sharpUnfilled` outlined. |
20
+ | trailingIconVariant | `"sharpUnfilled" \| "sharpFilled"` | `"sharpUnfilled"` | Fill style for the trailing icon: `sharpFilled` solid, `sharpUnfilled` outlined. |
21
+ | fullWidth | `boolean` | `false` | Stretches the button to 100% of its parent width instead of sizing to its label. Use it for card and form actions that should fill their column. |
22
+ | loading | `boolean` | `false` | Shows a centered spinner while the label and icons stay in layout at opacity 0 so the width does not jump. Blocks activation including form submit, sets `aria-busy` / `aria-disabled`, and keeps enabled visuals even when `disabled` is also true. Set `aria-label` to change what is announced while loading. |
23
+ | "aria-label" | `string` | `""` | Accessible name that overrides the visible label; set it when `children` is empty or too terse to describe the action. For icon-only actions reach for `IconButton` with its own `aria-label` instead. |
24
+ | children | `ReactNode` | `—` | Visible label, always rendered with `Text` `type=\"Button\"`. Provide meaningful label text; when it is empty or too terse, set `aria-label` or `aria-labelledby`. |
25
+ | disabled | `boolean` | `false` | Blocks activation and applies disabled styling through the native `disabled` attribute. For work in progress use `loading` instead — it keeps enabled visuals and overrides this prop. |
26
+
27
+ <!-- prettier-ignore-end -->
28
+
29
+ Extends `ButtonHTMLAttributes` — supports `disabled`, `onClick`, `className`, etc.
30
+
31
+ ## Usage
32
+
33
+ ```tsx
34
+ <Button emphasis="high" size="medium">
35
+ Click me
36
+ </Button>
37
+ <Button emphasis="medium" trailingIcon trailingIconName="chevron_right" trailingIconVariant="sharpUnfilled">
38
+ Button
39
+ </Button>
40
+ <Button emphasis="low">Text button</Button>
41
+ <Button leadingIcon trailingIcon>With both icons</Button>
42
+ <Button fullWidth emphasis="medium">Full width</Button>
43
+ <Button loading>Saving</Button>
44
+ ```
45
+
46
+
47
+ ## Notes
48
+
49
+ - The visible label is always rendered with `Text` `type="Button"` (from `children`).
50
+ - **Accessible name**: Provide meaningful label text via `children`. When visible text is not descriptive enough, set `aria-label` or `aria-labelledby`. For icon-only actions, use `IconButton` with `aria-label` instead of `Button` without children.
51
+ - **Loading**: Set `loading` while an action is in progress. Label/icons remain in the layout at opacity 0 so width is stable; a centered spinner is shown. The control sets `aria-busy` / `aria-disabled`, keeps enabled visuals (including when `disabled` is also true), and blocks activation (click, keyboard, and form submit) until loading ends. Prefer `loading` alone for pending work — do not rely on `disabled` for that state. The accessible name stays the label (or `aria-label`); set `aria-label` to customize what assistive tech announces while loading.
52
+ - **Full width**: Set `fullWidth` when the button should span its parent (e.g. card actions).
53
+ - **Focus**: Native outline on `:focus-visible` (keyboard only)
54
+ - **Keyboard**: Native `<button>` supports Enter and Space
@@ -0,0 +1,43 @@
1
+ # ButtonGroup
2
+
3
+ Import: `import { ButtonGroup } from "@uxelle/components";`
4
+
5
+ Groups related Button components with consistent spacing. Set `aria-label` when the group needs an accessible name.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | children | `ReactNode` | `—` | One `Button` per action, listed in the tab order you want. Any other node is dropped from the output (with a development warning), so wrap other controls in `Button` first. |
14
+ | direction | `"row" \| "row-reverse" \| "column" \| "column-reverse"` | `"row"` | Main-axis flow of the buttons. `row` and `column` follow DOM order, while `row-reverse` and `column-reverse` flip the visual order only — focus still moves through the children in the order you pass them. |
15
+ | fullWidth | `boolean` | `false` | Stretches the group to the full width of its container and gives every button an equal share of the main axis. Left unset, the group hugs its content and each button keeps its intrinsic size. |
16
+ | "aria-label" | `string` | `""` | Accessible name announced for the whole group, for example `Form actions`. Set this or `aria-labelledby` — otherwise the component logs a development warning, since the group role alone announces nothing. |
17
+
18
+ <!-- prettier-ignore-end -->
19
+
20
+ Extends `HTMLAttributes` — supports standard HTML attributes.
21
+
22
+ ## Usage
23
+
24
+ ```tsx
25
+ <ButtonGroup aria-label="Form actions">
26
+ <Button emphasis="medium">Cancel</Button>
27
+ <Button emphasis="high">Save</Button>
28
+ </ButtonGroup>
29
+ ```
30
+
31
+
32
+ ## Notes
33
+
34
+ - **What this is**
35
+ - A **layout group** for multiple independent actions (e.g. Cancel + Save).
36
+ - Compose only `Button` children; each button keeps its own action and focus ring.
37
+ - **What this is not**
38
+ - Not a **segmented control** or **toggle group** (no single selection, no `aria-pressed` roving pattern).
39
+ - For mutually exclusive options, use **SegmentedControl** or a radio group.
40
+ - Control how many buttons appear by **which `Button` children you pass** (two or three, etc.).
41
+ - **Accessibility**
42
+ - Uses `role="group"` by default. When buttons are related actions, set `aria-label` or `aria-labelledby`.
43
+ - Tab order follows **DOM order** of `Button` children. Order children for the intended keyboard sequence (e.g. secondary before primary left-to-right) even when `direction` is `row-reverse` (visual-only reversal).
@@ -0,0 +1,35 @@
1
+ # Card
2
+
3
+ Import: `import { Card } from "@uxelle/components";`
4
+
5
+ Elevated surface container for arbitrary content.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | cardContentSlot | `ReactNode` | `null` | Everything rendered inside the card surface — any layout, media, or controls. This is the only content prop: `Card` takes no `children`. Empty or whitespace-only content renders no inner wrapper, so the card stays blank. |
14
+ | padding | `"Default" \| "None"` | `"Default"` | Inner spacing around the content region. `Default` applies theme spacing; `None` removes it so media can bleed to the edges. The card keeps its stroke and elevation either way. |
15
+
16
+ <!-- prettier-ignore-end -->
17
+
18
+ ## Usage
19
+
20
+ ```tsx
21
+ <Card
22
+ cardContentSlot={
23
+ <Text type="Body Medium">Any content fits here.</Text>
24
+ }
25
+ />
26
+ ```
27
+
28
+ ```tsx
29
+ <Card padding="None" cardContentSlot={<Image src="/hero.jpg" alt="" aspectRatio="16:9" />} />
30
+ ```
31
+
32
+
33
+ ## Notes
34
+
35
+ - Use for generic grouped content that needs a bordered, elevated surface. Pass composed children through `cardContentSlot`. Empty content is not rendered. Set `padding="None"` when media or layout should bleed to the edges. Remaining HTML attributes—including `aria-*`, `id`, and `className`— are forwarded to the root element. Prefer `ProductCard` when the layout needs fixed top, center, and bottom regions.
@@ -0,0 +1,57 @@
1
+ # Checkbox
2
+
3
+ Import: `import { Checkbox } from "@uxelle/components";`
4
+
5
+ Native checkbox built as `<label>` + `<input type="checkbox">`.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | checked | `boolean` | `false` | Checked state — controlled when paired with `onChange`, otherwise only the initial value. Ignored inside CheckboxGroup, where the group's `value` decides which options are checked. |
14
+ | value | `string` | `""` | Value submitted with the form, and the identity this option is matched against in CheckboxGroup `value`. An empty string is valid; inside a group without one, a generated fallback is used that will never match your data. |
15
+ | indeterminate | `boolean` | `false` | Tri-state dash for partial selection, mapped to the native `indeterminate` property so assistive tech reports `mixed`. Independent of `checked` — a select-all uses `checked={allSelected}` with `indeterminate={someSelected && !allSelected}`. |
16
+ | disabled | `boolean` | `false` | Blocks interaction, applies disabled styling, and removes the control from the tab order. The native `disabled` attribute is set, so the value is not submitted. |
17
+ | embedded | `boolean` | `false` | Indicator-only mode for a checkbox nested in a larger target such as a ListItem row: no hover, active, or focus ring, and `tabIndex={-1}`. The parent must own focus, the accessible name, and keyboard activation. |
18
+ | onChange | `ChangeEventHandler<HTMLInputElement>` | `—` | Native change handler receiving the input event; read `event.target.checked` for the next state. Supplying it is what makes `checked` controlled, and inside **CheckboxGroup** it runs after the group's own handler. |
19
+ | label | `boolean` | `true` | Renders `children` beside the box and points `aria-labelledby` at that text. With `false` there is no visible name, so set `aria-label` or `aria-labelledby` on `inputProps` or the option is left unnamed. |
20
+ | children | `ReactNode` | `—` | Label content beside the box, usually a `Text` node; it becomes the accessible name through `aria-labelledby`. Not rendered when `label` is `false` or `embedded` is set. |
21
+ | id | `string` | `—` | Id applied to the `<input>` and matched by the wrapping label's `htmlFor`. Omit it for a generated id; set it when other elements or tests need a stable hook. |
22
+ | className | `string` | `—` | Class names merged onto the root `<label>`, applied before `labelProps.className` so both survive. The `<input>` keeps its own classes. |
23
+ | labelProps | `Omit<LabelHTMLAttributes<HTMLLabelElement>, "htmlFor" \| "children">` | `—` | Extra attributes spread onto the root `<label>` wrapper — use it for layout hooks, `data-*`, and pointer handlers, not form semantics. `htmlFor` and `children` are managed for you, so they cannot be passed. |
24
+ | inputProps | `CheckboxInputProps` | `—` | Attributes spread onto the `<input>` before the component's own; put form semantics here (`required`, `name`, `value`, `form`, `autoComplete`, ARIA). `type`, `checked`, `disabled`, `defaultChecked`, `onChange`, and `ref` are managed for you. |
25
+ | "inputProps.aria-describedby" | `string` | `""` | Space-separated ids of elements describing this one option, such as a price or caveat under the label. Group-level helper copy belongs on CheckboxGroup instead. |
26
+ | "inputProps.aria-label" | `string` | `""` | Accessible name for the option when `label` is false and no visible text exists. Skip it whenever visible label `children` can name the control instead. |
27
+ | "inputProps.aria-labelledby" | `string` | `""` | Space-separated ids of elements naming this option. Merged after the id generated for visible label `children`, so both are announced. |
28
+
29
+ <!-- prettier-ignore-end -->
30
+
31
+ ## Usage
32
+
33
+ ```tsx
34
+ <Checkbox>
35
+ {/* Content */}
36
+ </Checkbox>
37
+ ```
38
+
39
+ ## Notes
40
+
41
+ - **Accessibility (WCAG 2.x AA baseline)**
42
+ - **Keyboard** — Native `<input type="checkbox">` (implicit **`role="checkbox"`**; **`checked`** exposes state—do not set **`aria-checked`**). **Enter** / **Space** toggle while focused. **`embedded`** uses **`tabIndex={-1}`**.
43
+ - **Option naming** — Visible label text is referenced with **`aria-labelledby`**. Per-option helper copy via **`inputProps`** **`aria-describedby`**.
44
+ - **Disabled** — Sets HTML **`disabled`** and **`aria-disabled`**; the control is removed from the tab order.
45
+ - **Focus visible** — Keyboard focus is shown with a **`:focus-visible`** outline on the **root `<label>`** (the full hit target), using the design-system focus ring token. **`embedded`** omits the ring; the embedding control must expose a single clear focus indicator.
46
+ - **Name and state** — Checked and indeterminate state are exposed via the native control (not only icon color). The icon is **decorative / redundant** with that state and is marked **`aria-hidden`**. Do not set **`aria-checked`** manually—the browser maps **`indeterminate`** to **`mixed`**. Provide an **accessible name** with visible **`children`**, **`inputProps`** (**`aria-label`** / **`aria-labelledby`**), or **`labelProps`** as appropriate. When **`label={false}`** (no visible label text in this component), set a name on **`inputProps`** so the control is not unnamed.
47
+ - **Indeterminate** — Pass **`indeterminate`** for tri-state visuals (e.g. “select all” with partial selection). Uses the native **`HTMLInputElement.indeterminate`** property. On user activation, the browser clears indeterminate before **`onChange`**; when **`indeterminate`** is controlled, set it to **`false`** in **`onChange`** (or derive it from selection state).
48
+ - **Form / validation** — Pass **`required`**, **`aria-invalid`**, **`aria-describedby`**, **`aria-errormessage`**, and **`autoComplete`** on **`inputProps`**.
49
+ - **`labelProps` vs `inputProps`**
50
+ - Spread **`labelProps`** on the root `<label>`. Spread **`inputProps`** on the `<input>` **first**, then the component applies authoritative props (`type`, `checked`, `disabled`, `onChange`, `id`), so those always win.
51
+ - Put **form and control** semantics on **`inputProps`**, not on **`labelProps`**: `aria-describedby`, `autoComplete`, `form`, `name`, `value`. When **not** **`embedded`**, you may pass **`tabIndex`** on **`inputProps`**; disabled options always use **`tabIndex={-1}`**.
52
+ - **Controlled vs uncontrolled**
53
+ - **Controlled**: use **`checked`** with **`onChange`**. Pass **`indeterminate`** when tri-state is driven by the parent; clear or recompute it in **`onChange`** after the user toggles the control.
54
+ - **Uncontrolled**: omit **`onChange`**; internal state is seeded from **`checked`** when it is defined, otherwise the option starts unchecked and toggles on interaction. Initial **`indeterminate`** is cleared automatically on user interaction when **`indeterminate`** is not controlled.
55
+ - **Inside CheckboxGroup** — Group **`value`** / **`defaultValue`** own checked state; option **`checked`** is ignored. Set **`value`** on each option for a stable native value (group **`optionValues`** is a deprecated fallback).
56
+ - **Embedded mode**
57
+ - The checkbox is not in the tab order (`tabIndex={-1}`). The **embedding parent** must be focusable, have an accessible name, handle keyboard activation, and must not duplicate this control in the tab order.
@@ -0,0 +1,47 @@
1
+ # CheckboxGroup
2
+
3
+ Import: `import { CheckboxGroup } from "@uxelle/components";`
4
+
5
+ Fieldset-backed checkbox group; semantics and a11y rules are documented on **CheckboxGroupProps**.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | children | `ReactNode` | `—` | The `Checkbox` options for this group, in DOM order. The group owns checked state through `value` / `defaultValue`, so each child only needs its own `value` and its label content; nesting them in fragments or wrapper elements is fine. |
14
+ | name (required) | `string` | `—` | Shared form field `name` applied to every nested **Checkbox**, so each checked option is submitted as a separate entry under that one name. |
15
+ | value | `readonly string[]` | `—` | `value` of every checked option, which makes the group controlled. Pair with `onChange`; omit it to let the group track its own selection from `defaultValue`. |
16
+ | defaultValue | `readonly string[]` | `[] as readonly string[]` | Values checked on first render when uncontrolled. Read only on mount — later changes are ignored, so remount with a `key` or switch to controlled `value` to reset. |
17
+ | onChange | `(value: string[]) => void` | `—` | Receives the full next array of checked values whenever any option toggles, not just the one that changed. Required for controlled use alongside `value`. |
18
+ | label | `boolean` | `false` | Renders the `<legend>` that names the fieldset for assistive technology. Needs non-empty `labelText`; with `false`, name the group with `aria-label` or `aria-labelledby` instead or it is left unnamed. |
19
+ | labelText | `string` | `""` | Text inside the `<legend>`. Blank or whitespace-only text hides it entirely, leaving the group unnamed unless you pass `aria-label` or `aria-labelledby`. |
20
+ | fieldMessage | `ReactNode` | `—` | Helper or validation content rendered below the options. Strings and numbers are wrapped in a **FieldMessage** styled by `fieldMessageType`; any other node renders as-is, and its container id is linked to the fieldset automatically. |
21
+ | fieldMessageType | `"Info" \| "Danger" \| "Warning" \| "Success"` | `CHECKBOX_GROUP_DEFAULTS.fieldMessageType` | Tone and icon for a string or number `fieldMessage`. `Danger` also marks the group invalid, links the message with `aria-errormessage`, and announces it politely; other types link it with `aria-describedby`. |
22
+ | required | `boolean` | `false` | Sets `aria-required` on the fieldset so at least one selection is announced as mandatory. Nothing is enforced natively — validate the selected values yourself and report the failure through `fieldMessage`. |
23
+ | invalid | `boolean` | `false` | Marks the group invalid with `aria-invalid` unless `aria-invalid` is passed explicitly. Redundant when `fieldMessageType` is `Danger`, which already implies it. |
24
+ | "aria-label" | `string` | `""` | Accessible name used when no `<legend>` is rendered. Ignored while `label` and `labelText` show one, since the native legend names the fieldset. |
25
+ | "aria-labelledby" | `string` | `""` | Space-separated ids of elements that name the group when no `<legend>` is rendered. Dropped entirely while a legend is shown, to avoid a double announcement. |
26
+ | "aria-describedby" | `string` | `""` | Space-separated ids of helper or instruction copy. Merged with the field message region id for non-error messages (Info, Warning, Success). |
27
+ | "aria-errormessage" | `string` | `""` | Id of the element holding the error text. Defaults to the field message region id when `fieldMessageType` is `Danger`, and is only announced while the group is also marked invalid. |
28
+ | "aria-invalid" | `boolean` | `false` | Explicit `aria-invalid` on the fieldset, overriding whatever the component would infer. Omit it to let `invalid` or a `Danger` field message drive the state. |
29
+ | "aria-required" | `boolean` | `false` | Explicit `aria-required` on the fieldset, overriding what `required` would set. Use it when the requirement is conditional and `required` cannot express it. |
30
+
31
+ <!-- prettier-ignore-end -->
32
+
33
+ ## Usage
34
+
35
+ ```tsx
36
+ <CheckboxGroup
37
+ name="notifications"
38
+ label
39
+ labelText="Notification preferences"
40
+ defaultValue={["email"]}
41
+ onChange={(next) => setSelected(next)}
42
+ >
43
+ <Checkbox value="email"><Text type="Body Medium" width>Email updates</Text></Checkbox>
44
+ <Checkbox value="product"><Text type="Body Medium" width>Product announcements</Text></Checkbox>
45
+ </CheckboxGroup>
46
+ ```
47
+
@@ -0,0 +1,44 @@
1
+ # ChoiceChip
2
+
3
+ Import: `import { ChoiceChip } from "@uxelle/components";`
4
+
5
+ Toggle pill for choice sets (facets, sort, view options).
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | label (required) | `string` | `"Choice Chip"` | Visible chip text, and the accessible name unless `aria-label` overrides it. Whitespace is trimmed, and an empty result renders no chip at all. |
14
+ | disabled | `boolean` | `false` | Blocks toggling and applies disabled styling. Clicks fire no handlers, `onClick` included. |
15
+ | checked | `boolean` | `false` | Selected state when controlled — sets `aria-pressed` and swaps the leading icon for a check. Pass with `onChange`; omit it to let the chip track its own state from `defaultChecked`. |
16
+ | defaultChecked | `boolean` | `false` | Selected state on first render when `checked` is omitted. Read only on mount, so later changes to it are ignored. |
17
+ | leadingIcon | `boolean` | `false` | Adds a decorative icon before the label while the chip is unchecked. A checked chip always shows a check instead, whatever this is set to. |
18
+ | leadingIconName | `string` | `"add"` | Material Symbol drawn as the leading icon while unchecked. Ignored unless `leadingIcon` is `true`, and always replaced by `check` once the chip is checked. |
19
+ | leadingIconVariant | `"sharpUnfilled" \| "sharpFilled"` | `"sharpUnfilled"` | Glyph weight of the leading icon: `sharpFilled` is the solid form, `sharpUnfilled` the outlined one. The check on a checked chip is always filled. |
20
+ | trailingIcon | `boolean` | `false` | Adds a decorative icon after the label, for affordances such as a dropdown caret. It is not a separate control — the whole chip is one button. |
21
+ | trailingIconName | `string` | `"add"` | Material Symbol drawn after the label. Ignored unless `trailingIcon` is `true`. |
22
+ | trailingIconVariant | `"sharpUnfilled" \| "sharpFilled"` | `"sharpFilled"` | Glyph weight of the trailing icon: `sharpFilled` is the solid form, `sharpUnfilled` the outlined one. |
23
+ | onChange | `(checked: boolean) => void` | `—` | Fires on click with the next selected value (the inverse of the current one). Use this rather than `onClick` to drive `checked` and app selection state. |
24
+ | onClick | `MouseEventHandler<HTMLButtonElement>` | `—` | Native click handler, called after `onChange` with the raw mouse event. Reach for it only for side effects such as analytics; use `onChange` for selection. |
25
+ | "aria-label" | `string` | `""` | Overrides `label` as the accessible name, for cases where the visible text is abbreviated. Leave it unset when `label` already reads well on its own. |
26
+ | "aria-labelledby" | `string` | `""` | IDs of elements that name the chip instead of `label`, space-separated. Use it when the name already exists elsewhere on the page. |
27
+ | "aria-describedby" | `string` | `""` | IDs of elements holding supplementary description read after the name, space-separated. For hints, not for the chip's own name. |
28
+
29
+ <!-- prettier-ignore-end -->
30
+
31
+ ## Usage
32
+
33
+ ```tsx
34
+ <ChoiceChip label="Relevance" onChange={() => {}} />
35
+ ```
36
+
37
+ ```tsx
38
+ <ChoiceChip label="Newest" checked trailingIcon onChange={() => {}} />
39
+ ```
40
+
41
+
42
+ ## Notes
43
+
44
+ - Uses `checked` / `defaultChecked` with `aria-pressed`. Checked chips show a leading check. Selection policy (single vs multi) is owned by the app — this control only toggles itself. Compose in **ChoiceChipGroup**. For exclusive single-select, keep one checked value in app state and set `role="radiogroup"` on the group. Use **FilterChip** for dismissible applied filters. There is no InputChip — compose entry (e.g. Textfield) with FilterChips for removable values. Controlled: set `checked` from `onChange` (next pressed value). Uncontrolled: `defaultChecked` is mount-only.