@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,35 @@
1
+ # FilterChip
2
+
3
+ Import: `import { FilterChip } from "@uxelle/components";`
4
+
5
+ Removable pill for an applied filter value.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | label (required) | `string` | `"Filter Chip"` | Filter value shown on the chip; also seeds the remove control's default name (`Remove {label}`). Whitespace is trimmed, an empty result renders no chip, and long values are not truncated. |
14
+ | disabled | `boolean` | `false` | Applies disabled styling and blocks dismissal — the remove control ignores clicks and keys, and arrow-key navigation inside **FilterChipGroup** skips it. |
15
+ | leadingIcon | `boolean` | `false` | Adds a decorative icon before the label, useful for showing which facet the value belongs to. |
16
+ | leadingIconName | `string` | `"add"` | Material Symbol drawn before the label. Ignored unless `leadingIcon` is `true`. |
17
+ | leadingIconVariant | `"sharpUnfilled" \| "sharpFilled"` | `"sharpUnfilled"` | Glyph weight of the leading icon: `sharpFilled` is the solid form, `sharpUnfilled` the outlined one. |
18
+ | onRemove (required) | `(event: SyntheticEvent<HTMLButtonElement>) => void` | `—` | Fires when the user dismisses the chip — clicking the remove control, or pressing Enter, Space, Delete, or Backspace while it is focused. Drop the value from your own state here; the chip does not remove itself. |
19
+ | removeAriaLabel | `string` | `""` | Accessible name for the icon-only remove button. Falls back to `Remove {label}`, so set it only when the label alone would be ambiguous out of context. Overridden when `aria-label` or `aria-labelledby` is set. |
20
+ | "aria-label" | `string` | `""` | Accessible name for the remove control. Overrides `removeAriaLabel`. Ignored when `aria-labelledby` is set (browsers prefer labelledby). |
21
+ | "aria-labelledby" | `string` | `""` | IDs of elements that name the remove control, space-separated. Takes precedence over `aria-label` and `removeAriaLabel`. |
22
+ | "aria-describedby" | `string` | `""` | IDs of elements holding extra description for the remove control, space-separated. |
23
+
24
+ <!-- prettier-ignore-end -->
25
+
26
+ ## Usage
27
+
28
+ ```tsx
29
+ <FilterChip label="Region" leadingIcon leadingIconName="location_on" onRemove={() => {}} />
30
+ ```
31
+
32
+
33
+ ## Notes
34
+
35
+ - Always requires `onRemove` — the chip’s job is to represent a filter that can be dismissed. Root is a non-interactive `span` so the dismiss Icon Button nests correctly; the surface does not use hover/pressed fills. Prefer **ChoiceChip** for selectable option sets. There is no InputChip — compose entry (e.g. Textfield) with FilterChips for removable values. Keyboard: focus the remove control, then **Enter** / **Space** or **Delete** / **Backspace** to dismiss. Inside **FilterChipGroup**, arrow keys move between chips (one Tab stop for the set). Removal is announced via a polite live region.
@@ -0,0 +1,42 @@
1
+ # FilterChipGroup
2
+
3
+ Import: `import { FilterChipGroup } from "@uxelle/components";`
4
+
5
+ Groups dismissible applied-filter chips under an optional legend, wrapping to new rows as needed.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | children | `ReactNode` | `—` | **FilterChip** elements only. They render in a wrapping row inside the fieldset, above `fieldMessage`. |
14
+ | label | `boolean` | `true` | Renders a `legend` naming the group when `true` and `labelText` is non-empty. When `false`, name the group with `aria-label` or `aria-labelledby` instead. |
15
+ | labelText | `string` | `"Filters"` | Text shown in the `legend` while `label` is `true`. It is trimmed first, so a blank string suppresses the legend and the group then needs `aria-label`. |
16
+ | fieldMessage | `ReactNode` | `—` | Validation or helper content rendered below the chips. A string or number is wrapped in **FieldMessage** using `fieldMessageType`; any other node renders as-is. When `invalid` is true, a Danger field message is shown (this value when provided, otherwise a default error string). |
17
+ | fieldMessageType | `"Info" \| "Danger" \| "Warning" \| "Success"` | `FILTER_CHIP_GROUP_DEFAULTS.fieldMessageType` | Tone of the wrapped **FieldMessage** — `Info`, `Danger`, `Warning`, or `Success`. Forced to `Danger` while `invalid` is true. `Danger` wires the message as `aria-errormessage` and announces it politely; other tones link through `aria-describedby`. |
18
+ | required | `boolean` | `false` | Sets `aria-required` on the fieldset and shows the **Label** asterisk while a legend is visible. No native validation is enforced — the app owns that. |
19
+ | invalid | `boolean` | `false` | Sets `aria-invalid="true"` and shows a Danger field message (uses `fieldMessage` when set, otherwise a default error string). Also wires `aria-errormessage` and announces the message politely. `aria-invalid={true}` does the same; `false` is treated as omitted so this prop can still apply. |
20
+ | role | `string` | `"group"` | ARIA role on the fieldset. Leave it as `group`: the chips are independent dismissible filters, not a selection set. |
21
+ | "aria-label" | `string` | `""` | Accessible name for the fieldset, used only when no legend is showing. Prefer a visible legend via `label` and `labelText`; this value is dropped while one renders. |
22
+ | "aria-labelledby" | `string` | `""` | IDs of elements that name the fieldset when no legend renders, space-separated. Dropped while a legend is showing, since the legend already names the group. |
23
+ | "aria-describedby" | `string` | `""` | IDs of elements describing the group, space-separated. Merged with the field-message region id for non-`Danger` messages rather than replacing it. |
24
+ | "aria-errormessage" | `string` | `""` | ID of the element holding the error text. Defaults to the field-message region once the resolved message type is Danger, and is only announced while the group is also invalid. An empty string is treated as omitted so that default still applies. |
25
+ | "aria-invalid" | `boolean` | `false` | Explicit `aria-invalid` on the fieldset. `true` marks the group invalid and triggers the Danger field message; `false` is treated as omitted so `invalid` can still apply. A `Danger` field message alone does not mark the group invalid. |
26
+ | "aria-required" | `boolean` | `false` | Explicit `aria-required` on the fieldset. `true` marks a selection mandatory; `false` is treated as omitted so `required` can still apply. |
27
+
28
+ <!-- prettier-ignore-end -->
29
+
30
+ ## Usage
31
+
32
+ ```tsx
33
+ <FilterChipGroup label labelText="Fruit">
34
+ <FilterChip label="Apple" onRemove={() => {}} />
35
+ <FilterChip label="Banana" onRemove={() => {}} />
36
+ </FilterChipGroup>
37
+ ```
38
+
39
+
40
+ ## Notes
41
+
42
+ - Pass **FilterChip** children. Root is a **`fieldset`** with default **`role="group"`**; name it with the legend (`label` + `labelText`) or `aria-label` / `aria-labelledby`. Keyboard: one Tab stop into the set; **Arrow** keys move between remove controls; **Delete** / **Backspace** dismisses the focused chip. Removals are announced politely. Form parity: **`fieldMessage`**, **`required`**, **`invalid`**, and merged **`aria-describedby`** / **`aria-errormessage`**. For choice option toggles, use **ChoiceChipGroup**. There is no InputChip — compose entry (e.g. Textfield) with FilterChips for removable values.
@@ -0,0 +1,34 @@
1
+ # Footer
2
+
3
+ Import: `import { Footer, DefaultTopMainContent, DefaultBottomMainContent } from "@uxelle/components";`
4
+
5
+ Two-tier footer for enterprise applications.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | topMainContent | `ReactNode` | `undefined` | Contents of the upper band, which sets a `brand-2` color context — typically link columns, CTAs, a social row, and approval copy. Pass `null` to drop the band entirely; omit it to render catalog demo content. |
14
+ | bottomMainContent | `ReactNode` | `undefined` | Contents of the lower band, which sets a `neutral` color context — typically a **Logo** brand lockup and legal copy. Pass `null` to drop the band entirely; omit it to render catalog demo content. |
15
+
16
+ <!-- prettier-ignore-end -->
17
+
18
+ Extends `HTMLAttributes` — supports standard HTML attributes.
19
+
20
+ ## Usage
21
+
22
+ ```tsx
23
+ <Footer
24
+ topMainContent={<DefaultTopMainContent />}
25
+ bottomMainContent={<DefaultBottomMainContent />}
26
+ />
27
+ ```
28
+
29
+
30
+ ## Notes
31
+
32
+ - **Top section**: Main content slot (`topMainContent`) — columns, CTAs, social, approval.
33
+ - **Bottom section**: Branding and legal copy (`bottomMainContent`).
34
+ - Pass `null` on a slot to hide that band. Omit the `Footer` when the page has none.
@@ -0,0 +1,61 @@
1
+ # Hero
2
+
3
+ Import: `import { Hero } from "@uxelle/components";`
4
+
5
+ Full-bleed marketing band with overlaid copy and actions.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | contentDirection | `"Left" \| "Right" \| "Bottom"` | `"Left"` | Where the content panel sits over the media. From the tablet breakpoint (678px) up, `Left` and `Right` pin a half-width, vertically centered panel to that edge, while `Bottom` keeps a full-width panel along the bottom. Below 678px all three collapse to the same full-width bottom panel. |
14
+ | image (required) | `ReactNode` | `—` | Full-bleed media behind the hero. Pass a **direct** child `Image`, `img`, `picture`, or `video` — extra wrappers are unsupported and will not fill the media layer. Prefer `Image` for source, alt text, and optional overlay. Do not rely on `Image` aspect ratio — Hero owns the responsive crop. Keep this region non-interactive; put actions in `contentSlot`. |
15
+ | contentSlot (required) | `ReactNode` | `—` | Overlay panel content, stacked in a column with theme gap — typically a `Lockup` plus a `ButtonGroup`. Required: the hero always renders the panel, and it is where any interactive content belongs. |
16
+ | "data-color-switcher" | `string` | `—` | Color-switcher palette applied to the hero root, which drives the content panel surface and text colors. Falls back to `brand-1`; pass another palette name such as `brand-2` or `neutral` to override it. |
17
+
18
+ <!-- prettier-ignore-end -->
19
+
20
+ ## Usage
21
+
22
+ ```tsx
23
+ <Hero
24
+ contentDirection="Left"
25
+ image={<Image src="/hero.jpg" alt="Office building facade" />}
26
+ contentSlot={
27
+ <>
28
+ <Lockup
29
+ overline={<Text type="Overline" width={false}>Our mission</Text>}
30
+ title={
31
+ <Text type="Display Medium" as="h1" width={false}>
32
+ Health for all, Hunger for none
33
+ </Text>
34
+ }
35
+ body={
36
+ <Text type="Body Medium" width={false}>
37
+ Advancing access to health and nutrition for everyone, everywhere.
38
+ </Text>
39
+ }
40
+ />
41
+ <ButtonGroup aria-label="Actions">
42
+ <Button emphasis="high">
43
+ <Text type="Button" width={false}>
44
+ Primary action
45
+ </Text>
46
+ </Button>
47
+ <Button emphasis="medium">
48
+ <Text type="Button" width={false}>
49
+ Secondary action
50
+ </Text>
51
+ </Button>
52
+ </ButtonGroup>
53
+ </>
54
+ }
55
+ />
56
+ ```
57
+
58
+
59
+ ## Notes
60
+
61
+ - Pass media in `image` as a direct child `Image`, `img`, `picture`, or `video` (wrappers are unsupported) and composed intro content in `contentSlot` (usually `Lockup` and `ButtonGroup`). `contentDirection` controls panel placement: `Left` / `Right` for side panels on tablet and up, `Bottom` for a full-width bottom panel at every size. Below the tablet breakpoint, `Left` and `Right` use the bottom panel layout. Content-area spacing remaps through theme breakpoint aliases that currently start at 375px — widths below that need a theme update before spacing sign-off. The root defaults to `data-color-switcher="brand-1"`; pass a different attribute to override. Do not add a landmark role by default — pass `role="region"` with `aria-label` or `aria-labelledby` when a named region is needed. Remaining HTML attributes are forwarded to the root element.
@@ -0,0 +1,43 @@
1
+ # Icon
2
+
3
+ Import: `import { Icon } from "@uxelle/components";`
4
+
5
+ Renders Material Symbols icons.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | iconName | `string` | `"add"` | Material Symbols ligature name to render, e.g. `home`, `close`, or `settings`. Names outside that set render as literal text. |
14
+ | size | `number` | `24` | Glyph size in pixels, applied as an inline `font-size`. Valid range is 8–128 and out-of-range values warn in development; leaving it at the default emits no inline size, so CSS or a parent token can drive it. |
15
+ | style | `CSSProperties` | `—` | Inline styles merged onto the icon `span`, applied after the `size`-derived `font-size` — a `fontSize` here therefore overrides `size`. |
16
+ | "aria-label" | `string` | `""` | Accessible name for an icon that carries meaning. Setting it also suppresses the automatic `aria-hidden`, so the icon becomes visible to assistive technology. |
17
+ | "aria-hidden" | `"auto" \| "true" \| "false"` | `undefined` | Explicitly includes or excludes the icon from the accessibility tree. Leave it unset for the default: hidden when there is no `aria-label`, exposed once there is one. |
18
+ | role | `string` | `""` | Role placed on the icon `span`. Pair `img` with `aria-label` when the glyph is meaningful content; leave it unset for decorative icons. |
19
+ | variant | `"sharpUnfilled" \| "sharpFilled"` | `"sharpUnfilled"` | Glyph weight: `sharpFilled` renders the solid form through font-variation settings, `sharpUnfilled` the outlined form. |
20
+
21
+ <!-- prettier-ignore-end -->
22
+
23
+ ## Usage
24
+
25
+ ```tsx
26
+ // Decorative icon (default - hidden from screen readers)
27
+ <Icon iconName="home" size={24} />
28
+ ```
29
+
30
+ ```tsx
31
+ // Meaningful icon with accessible name
32
+ <Icon iconName="close" aria-label="Close dialog" size={24} />
33
+ ```
34
+
35
+ ```tsx
36
+ // Icon with custom role
37
+ <Icon iconName="settings" role="img" aria-label="Settings" size={32} />
38
+ ```
39
+
40
+
41
+ ## Notes
42
+
43
+ - Uses the material-symbols sharp font; updates instantly when `iconName` changes. By default, icons are hidden from screen readers (`aria-hidden="true"`) as they are typically decorative. Provide an `aria-label` when the icon conveys meaningful information.
@@ -0,0 +1,44 @@
1
+ # IconButton
2
+
3
+ Import: `import { IconButton } from "@uxelle/components";`
4
+
5
+ Compact button whose only visible content is an icon.
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` glyph-only for tertiary or inline actions. |
14
+ | activated | `boolean` | `false` | Applies the selected, persistently-on surface and sets `aria-pressed` unless you pass your own. Use it for toggles that stay on, not for hover or focus feedback. |
15
+ | size | `"extraSmall" \| "small" \| "medium" \| "large"` | `"medium"` | Padding around the glyph, so it sets the overall hit area: `extraSmall` removes padding entirely, `large` gives the most. The glyph itself never scales. |
16
+ | iconName | `string` | `"add"` | Material Symbol glyph shown in the button. It is `aria-hidden`, so whatever it conveys must also be spelled out in `aria-label`. |
17
+ | iconVariant | `"sharpUnfilled" \| "sharpFilled"` | `"sharpUnfilled"` | Fill style for the glyph: `sharpFilled` solid, `sharpUnfilled` outlined. |
18
+ | loading | `boolean` | `false` | Shows a centered spinner while the icon stays in layout at opacity 0 so the box does not resize. Blocks activation, 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. |
19
+ | "aria-label" | `string` | `""` | Accessible name for the button — effectively required, since the glyph is `aria-hidden` and contributes no text. Development builds warn when neither this nor `aria-labelledby` is set. |
20
+ | 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. |
21
+
22
+ <!-- prettier-ignore-end -->
23
+
24
+ Extends `ButtonHTMLAttributes` — supports `disabled`, `onClick`, `className`, etc.
25
+
26
+ ## Usage
27
+
28
+ ```tsx
29
+ <IconButton iconName="add" aria-label="Add item" />
30
+ <IconButton emphasis="medium" iconName="close" aria-label="Close" />
31
+ <IconButton emphasis="low" iconName="settings" aria-label="Settings" />
32
+ <IconButton emphasis="high" activated iconName="add" aria-label="Pinned" aria-pressed />
33
+ <IconButton loading iconName="save" aria-label="Saving" />
34
+ ```
35
+
36
+
37
+ ## Notes
38
+
39
+ - **WCAG AA compliant**: Contrast ratios meet 4.5:1 for icon
40
+ - **Browser-based focus ring**: Uses native outline on :focus-visible (keyboard only)
41
+ - **Keyboard operable**: Native <button> supports Enter and Space
42
+ - **ARIA**: Provide `aria-label` or `aria-labelledby` (the icon is `aria-hidden`). Development builds warn when neither is set.
43
+ - **Activated**: Set `activated` for selected/toggled-on visuals; pair with `aria-pressed` on toggle buttons (set automatically when `activated` is true)
44
+ - **Loading**: Set `loading` while an action is in progress. The icon remains in the layout at opacity 0 so size 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 until loading ends. Prefer `loading` alone for pending work. The accessible name stays `aria-label`; change that prop to customize what assistive tech announces while loading.
@@ -0,0 +1,40 @@
1
+ # Image
2
+
3
+ Import: `import { Image } from "@uxelle/components";`
4
+
5
+ Responsive image that optionally crops its source to a selected aspect ratio.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | src (required) | `string` | `""` | Source URL forwarded to the inner `img` element. Nothing renders in its place when the request fails, so guard missing values upstream. |
14
+ | alt (required) | `string` | `""` | Text alternative announced by assistive technology. Describe what the image conveys, or pass an empty string to mark it decorative and drop it from the accessibility tree. |
15
+ | aspectRatio | `"1:1" \| "4:3" \| "3:2" \| "16:9" \| "21:9" \| "3:4" \| "2:3"` | `undefined` | Crops the image to a fixed ratio — `1:1`, `4:3`, `3:2`, `16:9`, `21:9`, `3:4`, or `2:3`. The image fills the available width and covers the box, so overflow is trimmed; omit to keep the source's intrinsic proportions. |
16
+ | overlay | `"100" \| "200"` | `undefined` | Layers a decorative gradient wash over the image so text placed on top keeps contrast: `100` is a light wash, `200` a dark wash weighted to the bottom edge. Omit for no overlay. |
17
+
18
+ <!-- prettier-ignore-end -->
19
+
20
+ ## Usage
21
+
22
+ ```tsx
23
+ <Image src="/images/team.jpg" alt="Product team collaborating" aspectRatio="16:9" />
24
+ ```
25
+
26
+ ```tsx
27
+ <Image
28
+ src="/images/hero.jpg"
29
+ alt="Crop field at sunrise"
30
+ aspectRatio="21:9"
31
+ overlay="200"
32
+ />
33
+ ```
34
+
35
+
36
+ ## Notes
37
+
38
+ - When `aspectRatio` is set, the image fills its available width and uses `object-fit: cover` to preserve the ratio. Omit `aspectRatio` to keep the intrinsic proportions. Supply meaningful `alt` text when the image conveys information, or `alt=""` when it is decorative.
39
+ - Set `overlay` to `"100"` or `"200"` to layer a decorative gradient wash for contrast (for example under text). The wash is non-interactive and hidden from assistive technologies.
40
+ - This component is non-interactive. Wrap it in a link or button only when the surrounding experience requires an action, and name that control separately.
@@ -0,0 +1,35 @@
1
+ # Label
2
+
3
+ Import: `import { Label } from "@uxelle/components";`
4
+
5
+ Accessible caption that names a form field or input.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | required | `boolean` | `false` | Prefixes the label with a danger-colored asterisk marking the field as mandatory. The asterisk is `aria-hidden`, so set `required` on the control itself as well. |
14
+ | disabled | `boolean` | `false` | Switches the label and its asterisk to the disabled text color so they match a disabled control. Styling only — it does not block interaction, so set `disabled` on the control too. |
15
+ | fieldDescription | `string` | `""` | Helper copy rendered below the label as full-width `Condensed` **Text**; an empty string renders nothing. It gets a stable id (`{label id}-field-description`) to reference from the control's `aria-describedby`. |
16
+ | children | `ReactNode` | `—` | Label content, typically `Text`. Prefer `fieldDescription` for helper copy; extra `Text` children can be composed as well. |
17
+
18
+ <!-- prettier-ignore-end -->
19
+
20
+ Extends `React.LabelHTMLAttributes` — supports standard HTML attributes.
21
+
22
+ ## Usage
23
+
24
+ ```tsx
25
+ <Label required fieldDescription="Your full legal name">
26
+ <Text type="Body Medium Alt">Field name</Text>
27
+ </Label>
28
+ ```
29
+
30
+
31
+ ## Notes
32
+
33
+ - Composable: pass label content as `children` (typically Text). Prefer `fieldDescription` for helper copy; you can also add extra Text children for advanced composition.
34
+ - **Accessibility**: Renders as semantic `<label>`. Associate with controls via `htmlFor` / `id`. `fieldDescription` gets a stable `id` (`{label id}-field-description`) for `aria-describedby` on the control.
35
+ - **States**: Required (asterisk always uses `data-color-switcher="danger"`), disabled.
@@ -0,0 +1,58 @@
1
+ # LabelBadge
2
+
3
+ Import: `import { LabelBadge } from "@uxelle/components";`
4
+
5
+ Pill label with optional leading and trailing icons. Non-interactive; visible text is the accessible name (avoid duplicating it with `aria-label` unless the visible label is abbreviated).
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | label (required) | `ReactNode` | `"Label"` | Visible badge text, which is also the accessible name. A string is trimmed and wrapped in `Text` with `type="Condensed Alt"`; pass a `Text` node instead to pick a different type. Empty or whitespace-only renders nothing and warns in development. |
14
+ | emphasis | `"low" \| "high"` | `"low"` | Fill strength: `low` tints the badge background and keeps body-colored text, `high` uses a solid fill with text-on-fill color. Combine with `dataColorSwitcher` to make either strength carry a status color. |
15
+ | dataColorSwitcher | `string` | `—` | Status palette for this badge only, such as `success` for a delivered row. Sets `data-color-switcher` on the root so the fill and text resolve from that palette instead of the surrounding page palette. |
16
+ | "data-color-switcher" | `string` | `—` | Attribute spelling of `dataColorSwitcher`, for call sites that spread DOM props. Wins over `dataColorSwitcher` when both are set. |
17
+ | leadingIcon | `boolean` | `false` | Renders a 12px decorative icon before the label, chosen with `leadingIconName` and `leadingIconVariant`. Those two props have no effect while this is false. |
18
+ | trailingIcon | `boolean` | `false` | Renders a 12px decorative icon after the label, chosen with `trailingIconName` and `trailingIconVariant`. Those two props have no effect while this is false. |
19
+ | leadingIconName | `string` | `"add"` | Material Symbol rendered in the leading slot; applies only when `leadingIcon` is true. |
20
+ | trailingIconName | `string` | `"add"` | Material Symbol rendered in the trailing slot; applies only when `trailingIcon` is true. |
21
+ | leadingIconVariant | `"sharpUnfilled" \| "sharpFilled"` | `"sharpUnfilled"` | Leading glyph weight: `sharpUnfilled` is outlined, `sharpFilled` is solid. |
22
+ | trailingIconVariant | `"sharpUnfilled" \| "sharpFilled"` | `"sharpUnfilled"` | Trailing glyph weight: `sharpUnfilled` is outlined, `sharpFilled` is solid. |
23
+ | "aria-label" | `string` | `""` | Overrides the visible label as the accessible name. Use it only when the visible text is abbreviated or symbolic; leaving it unset keeps the two in sync. |
24
+ | "aria-labelledby" | `string` | `""` | Space-separated ids of elements that already name this badge elsewhere on the page. Takes the place of `aria-label`. |
25
+ | "aria-describedby" | `string` | `""` | Space-separated ids of elements read after the badge name as supporting detail, such as an explanation of what the status means. |
26
+ | role | `string` | `""` | ARIA role for the root span, which is otherwise a plain generic element. Static badges need none; `status` suits a badge whose text reports changing state. |
27
+ | "aria-live" | `"polite" \| "assertive" \| "off"` | `""` | How updates to the badge text are announced: `polite` waits for a pause, `assertive` interrupts. Leave unset for badges whose text never changes. |
28
+
29
+ <!-- prettier-ignore-end -->
30
+
31
+ ## Usage
32
+
33
+ ```tsx
34
+ <LabelBadge label="Badge" />
35
+ ```
36
+
37
+ ```tsx
38
+ <LabelBadge label={<Text type="Overline">Status</Text>} />
39
+ ```
40
+
41
+ ```tsx
42
+ <LabelBadge label="Badge" leadingIcon />
43
+ ```
44
+
45
+ ```tsx
46
+ <LabelBadge label="Badge" trailingIcon />
47
+ ```
48
+
49
+ ```tsx
50
+ <LabelBadge label="Badge" leadingIcon leadingIconName="star" leadingIconVariant="sharpFilled" />
51
+ ```
52
+
53
+
54
+ ## Notes
55
+
56
+ - **Label:** Pass a string (wrapped as **Text** `type="Condensed Alt"`) or a **Text** node to choose a different type.
57
+ - **Color:** Set `dataColorSwitcher` (or `data-color-switcher`) for status palettes (`success`, `warning`, `danger`, etc.). Low emphasis uses subtle status background + text; high uses fill + text-on-fill.
58
+ - **Accessibility:** Leading/trailing icons are decorative and omitted from the accessibility tree. Pass `aria-label`, `aria-labelledby`, or other attributes via props when needed for context. If the badge carries meaning on an interactive parent, mirror it on that focus target (e.g. `aria-label` on `IconButton`).
@@ -0,0 +1,38 @@
1
+ # LanguageSelector
2
+
3
+ Import: `import { LanguageSelector } from "@uxelle/components";`
4
+
5
+ Language selector dropdown for switching locale in enterprise applications.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | value | `string` | `"EN"` | Language code of the active locale, matched case-insensitively against `options` to pick the code shown on the trigger. Fully controlled — selecting an option only fires `onChange`, so update this value yourself. |
14
+ | onChange | `(code: string) => void` | `—` | Fires with the chosen option's `code` when the user picks a language, and closes the menu. Handle it by switching your locale and passing the new code back as `value`; without it the selection has no effect. |
15
+ | options | `LanguageOption[]` | `LANGUAGE_SELECTOR_DEFAULT_OPTIONS` | Languages listed in the dropdown, each a `{ code, label }` pair rendered as `code – label`. Omit to offer the built-in EN, ES, BR, and DE set. |
16
+
17
+ <!-- prettier-ignore-end -->
18
+
19
+ ## Usage
20
+
21
+ ```tsx
22
+ // With i18n (e.g. react-i18next)
23
+ <LanguageSelector
24
+ value={i18n.language}
25
+ onChange={(code) => i18n.changeLanguage(code)}
26
+ options={[
27
+ { code: "EN", label: "English" },
28
+ { code: "ES", label: "Español" },
29
+ ]}
30
+ />
31
+ ```
32
+
33
+
34
+ ## Notes
35
+
36
+ - **Controlled**: Parent owns the current locale (from i18n context, URL, or state) and passes `value`. On selection, `onChange` fires so the parent can update locale and re-render.
37
+ - **Trigger**: Shows current language code and chevron; opens menu on click.
38
+ - **Menu**: List of options in "code – label" format (e.g. "EN – English").
@@ -0,0 +1,34 @@
1
+ # LanguageSelectorButton
2
+
3
+ Import: `import { LanguageSelectorButton } from "@uxelle/components";`
4
+
5
+ Button used as the trigger for the Language Selector dropdown.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | languageCode | `string` | `"EN"` | Short code rendered between the globe and chevron icons, typically two letters such as `EN`. Inside **LanguageSelector** it is supplied automatically from that component's `value`. |
14
+ | open | `boolean` | `false` | Reflects the popup state: the chevron points up and `aria-expanded` becomes true when set, and points down when not. Drive it from the menu's open state so the trigger and popup stay in sync. |
15
+ | "aria-label" | `string` | `""` | Accessible name for the button. The visible content is only a language code, so supply something like `Select language. Current: EN`; **LanguageSelector** sets this for you. |
16
+ | trailingIconName | `string` | `—` | Accepted but never rendered — **Menu** passes it when cloning a trigger, and the chevron is always derived from `open`. Do not set it. |
17
+
18
+ <!-- prettier-ignore-end -->
19
+
20
+ Extends `ButtonHTMLAttributes` — supports `disabled`, `onClick`, `className`, etc.
21
+
22
+ ## Usage
23
+
24
+ ```tsx
25
+ <LanguageSelectorButton languageCode="EN" open={isOpen} />
26
+ ```
27
+
28
+
29
+ ## Notes
30
+
31
+ - **Globe icon**: Leading icon indicating language selection.
32
+ - **Language code**: Displays the current language (e.g. EN, ES).
33
+ - **Chevron**: Trailing icon; points down when closed, up when open.
34
+ - **States**: Uses language-selector theme tokens for hover, pressed, and activated.
@@ -0,0 +1,81 @@
1
+ # Layout
2
+
3
+ Import: `import { Layout } from "@uxelle/components";`
4
+
5
+ Flex and grid container with tokenized spacing shortcuts.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | p | `"none" \| "xs" \| "sm" \| "md" \| "lg" \| "xl"` | `undefined` | Padding on all four sides. Prefer a theme layout-spacing var (`var(--uxl-theme-layout-spacing-*)`) or `0`; named aliases (`none` | `xs` | `sm` | `md` | `lg` | `xl`), numbers as px, and raw CSS lengths also resolve. |
14
+ | pt | `"none" \| "xs" \| "sm" \| "md" \| "lg" \| "xl"` | `undefined` | Padding on the top edge only. Same values as `p`; wins over `pv` and `p`. |
15
+ | pr | `"none" \| "xs" \| "sm" \| "md" \| "lg" \| "xl"` | `undefined` | Padding on the right edge only. Same values as `p`; wins over `ph` and `p`. |
16
+ | pb | `"none" \| "xs" \| "sm" \| "md" \| "lg" \| "xl"` | `undefined` | Padding on the bottom edge only. Same values as `p`; wins over `pv` and `p`. |
17
+ | pl | `"none" \| "xs" \| "sm" \| "md" \| "lg" \| "xl"` | `undefined` | Padding on the left edge only. Same values as `p`; wins over `ph` and `p`. |
18
+ | ph | `"none" \| "xs" \| "sm" \| "md" \| "lg" \| "xl"` | `undefined` | Padding on the left and right edges, leaving top and bottom untouched. Same values as `p`; `pl` / `pr` win over it. |
19
+ | pv | `"none" \| "xs" \| "sm" \| "md" \| "lg" \| "xl"` | `undefined` | Padding on the top and bottom edges, leaving left and right untouched. Same values as `p`; `pt` / `pb` win over it. |
20
+ | m | `"none" \| "xs" \| "sm" \| "md" \| "lg" \| "xl"` | `undefined` | Margin on all four sides. Same values as `p` — a theme layout-spacing var or `0` is preferred over a named alias or raw length. |
21
+ | mt | `"none" \| "xs" \| "sm" \| "md" \| "lg" \| "xl"` | `undefined` | Margin on the top edge only. Same values as `p`; wins over `mv` and `m`. |
22
+ | mr | `"none" \| "xs" \| "sm" \| "md" \| "lg" \| "xl"` | `undefined` | Margin on the right edge only. Same values as `p`; wins over `mh` and `m`. |
23
+ | mb | `"none" \| "xs" \| "sm" \| "md" \| "lg" \| "xl"` | `undefined` | Margin on the bottom edge only. Same values as `p`; wins over `mv` and `m`. |
24
+ | ml | `"none" \| "xs" \| "sm" \| "md" \| "lg" \| "xl"` | `undefined` | Margin on the left edge only. Same values as `p`; wins over `mh` and `m`. |
25
+ | mh | `"none" \| "xs" \| "sm" \| "md" \| "lg" \| "xl"` | `undefined` | Margin on the left and right edges, leaving top and bottom untouched. Same values as `p`; `ml` / `mr` win over it. |
26
+ | mv | `"none" \| "xs" \| "sm" \| "md" \| "lg" \| "xl"` | `undefined` | Margin on the top and bottom edges, leaving left and right untouched. Same values as `p`; `mt` / `mb` win over it. |
27
+ | display | `"block" \| "flex" \| "grid" \| "inline" \| "inline-block" \| "inline-flex" \| "inline-grid"` | `"block"` | CSS `display` for the container, and the switch that decides which layout props are written: flex props apply only for `flex` / `inline-flex`, grid props only for `grid` / `inline-grid`, and `gap` for either. |
28
+ | gap | `"none" \| "xs" \| "sm" \| "md" \| "lg" \| "xl"` | `undefined` | Space between flex or grid children, ignored unless `display` is a flex or grid value. Same values as `p`; `rowGap` / `columnGap` override one axis. |
29
+ | width | `string` | `""` | Width of the box (border-box). Numbers become px; strings pass through as CSS (`100%`, `24rem`, `var(--uxl-…)`). Omit to size from content. |
30
+ | height | `string` | `""` | Height of the box (border-box). Numbers become px; strings pass through as CSS. Omit to size from content. |
31
+ | minWidth | `number \| string` | `—` | Floor for the rendered width. Numbers become px; strings pass through as CSS. |
32
+ | minHeight | `number \| string` | `—` | Floor for the rendered height. Numbers become px; strings pass through as CSS. |
33
+ | maxWidth | `number \| string` | `—` | Ceiling for the rendered width, letting the box shrink below `width` on narrow viewports. Numbers become px; strings pass through as CSS. |
34
+ | maxHeight | `number \| string` | `—` | Ceiling for the rendered height. Numbers become px; strings pass through as CSS. |
35
+ | style | `CSSProperties` | `—` | Escape hatch for CSS with no matching prop. Merged after the computed styles, so entries here override `display`, spacing, and size props. |
36
+ | as | `"div" \| "section" \| "main" \| "article" \| "aside" \| "span" \| "nav"` | `"div"` | Tag rendered for the container. Use `section`, `main`, `nav`, `article`, or `aside` to give the box document semantics instead of a generic `div`. |
37
+ | className | `string` | `—` | Extra classes appended after the base `uxl-layout` class, which always stays. |
38
+ | role | `string` | `—` | ARIA role for the container. Reach for it when `as` renders a generic `div` or `span` that needs a landmark or widget role; omit when `as` already carries the right semantics. |
39
+ | "aria-label" | `string` | `—` | Accessible name for a container that has a landmark or widget `role` but no visible text label. Prefer `aria-labelledby` when the name is already on screen. |
40
+ | "aria-describedby" | `string` | `—` | Space-separated ids of elements that describe the container; assistive technology announces them after the name. |
41
+ | "aria-labelledby" | `string` | `—` | Space-separated ids of elements whose text names the container. Wins over `aria-label` and keeps the name in sync with visible copy. |
42
+ | "aria-hidden" | `boolean` | `—` | When true, removes the container and its subtree from the accessibility tree while leaving it visible. Never set it on a box holding focusable elements. |
43
+ | "aria-live" | `"off" \| "polite" \| "assertive"` | `—` | Turns the container into a live region so asynchronous content updates are announced: `polite` waits for a pause, `assertive` interrupts, `off` stops announcements. |
44
+ | "aria-atomic" | `boolean` | `—` | When true, the live region is announced in full on every change instead of just the changed node. Only meaningful alongside `aria-live`. |
45
+ | "aria-relevant" | `"additions" \| "removals" \| "text" \| "all"` | `—` | Which mutations a live region announces — `additions`, `removals`, `text`, or `all`. Only meaningful alongside `aria-live`. |
46
+ | tabIndex | `number` | `—` | Keyboard reachability of the container: `0` puts it in DOM tab order, `-1` makes it focusable only from script. Omit for a non-interactive box. |
47
+ | children | `ReactNode` | `—` | Content the container lays out — the flex or grid items, or arbitrary nodes when `display` is `block`. |
48
+ | flexDirection | `"row" \| "row-reverse" \| "column" \| "column-reverse"` | `"column"` | Main-axis direction for flex children: `row` lays them out horizontally, `column` vertically. Applies only when `display` is `flex` or `inline-flex`. |
49
+ | flexWrap | `"nowrap" \| "wrap" \| "wrap-reverse"` | `"nowrap"` | Whether flex children wrap onto new lines instead of overflowing the main axis. Applies only when `display` is `flex` or `inline-flex`. |
50
+ | justifyContent | `"flex-start" \| "flex-end" \| "center" \| "space-between" \| "space-around" \| "space-evenly" \| "stretch"` | `"flex-start"` | Distribution of flex children along the main axis set by `flexDirection`. Applies only when `display` is `flex` or `inline-flex`. |
51
+ | alignItems | `"flex-start" \| "flex-end" \| "center" \| "baseline" \| "stretch"` | `"stretch"` | Alignment of flex children across the cross axis; `stretch` makes them fill it. Applies only when `display` is `flex` or `inline-flex` — grid containers use `gridAlignItems`. |
52
+
53
+ <!-- prettier-ignore-end -->
54
+
55
+ ## Usage
56
+
57
+ ```tsx
58
+ // Theme spacing tokens (preferred)
59
+ <Layout display="flex" gap="var(--uxl-theme-layout-spacing-medium-8)" p="var(--uxl-theme-layout-spacing-medium-12)" alignItems="center">
60
+ <div>Item 1</div>
61
+ <div>Item 2</div>
62
+ </Layout>
63
+ ```
64
+
65
+ ```tsx
66
+ // Grid layout
67
+ <Layout
68
+ display="grid"
69
+ gridTemplateColumns="1fr 1fr 1fr"
70
+ gap="var(--uxl-theme-layout-spacing-medium-12)"
71
+ >
72
+ <div>Column 1</div>
73
+ <div>Column 2</div>
74
+ <div>Column 3</div>
75
+ </Layout>
76
+ ```
77
+
78
+
79
+ ## Notes
80
+
81
+ - Props map to inline styles (`display`, flex/grid tracks, `gap`, and spacing shortcuts such as `p`, `ph`, `pt`). Prefer theme layout-spacing variables from `LAYOUT_SPACING_VARS` (`var(--uxl-theme-layout-spacing-*)` or `0`) for new UI. Named aliases (`none` | `xs` | `sm` | `md` | `lg` | `xl`) still resolve. Numbers stay px for back-compat; other unit strings pass through. `gap` applies when `display` is flex or grid.
@@ -0,0 +1,53 @@
1
+ # LinearProgress
2
+
3
+ Import: `import { LinearProgress } from "@uxelle/components";`
4
+
5
+ Horizontal progress bar with optional label, percentage, and field message.
6
+
7
+ ## Props
8
+
9
+ <!-- prettier-ignore-start -->
10
+
11
+ | Prop | Type | Default | Description |
12
+ |------|------|---------|-------------|
13
+ | progress | `"0" \| "20" \| "40" \| "60" \| "80" \| "100" \| "unknown"` | `0 as const satisfies LinearProgressProgress` | How full the bar is: `0`, `20`, `40`, `60`, `80`, `100`, or `Unknown` to switch to indeterminate. `Success` and `Danger` fill the track regardless and report `100` and `0`; indeterminate ignores the value entirely. |
14
+ | indeterminate | `boolean` | `false` | Runs the sliding animation used when work cannot be measured, dropping `aria-valuenow`. Also on when `progress` is `Unknown`, and forced off once `status` reaches the terminal `Success` or `Danger`. |
15
+ | status | `"Loading" \| "Success" \| "Warning" \| "Danger"` | `"Loading"` | Outcome the bar is reporting. `Loading` and `Warning` stay busy; `Success` and `Danger` are terminal and stop the animation. Drives track color, default label and footer copy, `aria-busy`, `aria-invalid`, and the live status text. |
16
+ | label | `boolean` | `true` | Renders the label row above the bar and names the progressbar through `aria-labelledby`. Also gates `labelSlot` and `fieldDescription`; with `false`, pass `aria-label` or `aria-labelledby` so the bar is not left unnamed. |
17
+ | required | `boolean` | `false` | Adds the required asterisk to the built-in `Label`. Visual only — a progressbar is not a form control, so no `aria-required` is emitted. |
18
+ | disabled | `boolean` | `false` | Applies disabled styling to the built-in `Label` only. The bar keeps animating and stays exposed to assistive technology. |
19
+ | labelSlot | `ReactNode` | `—` | Your own label region (typically a **Label**) in place of the built-in one, suppressing `labelTitle` and the supporting line. Still requires `label` to be `true`, and you must wire `aria-label` or `aria-labelledby` yourself. |
20
+ | labelTitle | `string` | `""` | Headline in the built-in label row, and the source of the progressbar's accessible name. An empty string falls back to sample copy for the current `status`; ignored when `labelSlot` is set. |
21
+ | fieldDescriptionContent | `string` | `""` | Supporting sentence under `labelTitle`. An empty string falls back to sample copy for the current `status`, and the line is linked with `aria-describedby` only when both it and `labelTitle` resolve to text. |
22
+ | fieldDescription | `boolean` | `true` | Shows the supporting line under `labelTitle`. Only takes effect while `label` is true; false hides that line without touching the footer message. |
23
+ | percentage | `boolean` | `true` | Shows the numeric percent column beside the track. Decorative only — the value is always exposed through `aria-valuenow` — and suppressed while indeterminate or when `status` is `Danger`. |
24
+ | fieldMessage | `boolean` | `true` | Renders the footer message row below the bar and links it to the progressbar with `aria-describedby`. Keep it on for indeterminate and `Danger`, which carry the only text describing the outcome; turning it off warns in development. |
25
+ | fieldMessageContent | `string` | `""` | Footer message text. An empty string falls back to sample copy for the current `status`; ignored when `fieldMessageSlot` replaces the footer. |
26
+ | fieldMessageSlot | `ReactNode` | `—` | Your own footer content in place of the built-in **FieldMessage**. Still requires `fieldMessage` to be `true`; the wrapper carries the `id` the progressbar points at with `aria-describedby`. |
27
+ | messageType | `"Info" \| "Success" \| "Warning" \| "Danger"` | `FIELD_MESSAGE_DEFAULTS.messageType` | Icon and tone for the built-in footer message. Omit to follow `status` (`Loading` maps to `Info`); ignored when `fieldMessageSlot` replaces the footer. |
28
+ | "aria-label" | `string` | `""` | Accessible name on the progressbar when the built-in label is hidden or insufficient (`label={false}`, `labelSlot`). Omit when `labelTitle` wires `aria-labelledby`. |
29
+ | "aria-labelledby" | `string` | `""` | Extra element IDs merged into progressbar `aria-labelledby` (space-separated). Built-in label id is added automatically. |
30
+ | "aria-describedby" | `string` | `""` | Extra element IDs merged into progressbar `aria-describedby` (space-separated). Supporting copy and footer ids are added automatically. |
31
+ | role | `string` | `""` | ARIA role on the track element. Defaults to `progressbar`; override only for exceptional cases. |
32
+ | "aria-live" | `"polite" \| "assertive" \| "off"` | `""` | Politeness of the visually hidden status live region. Unset uses `polite` (recommended). |
33
+ | className | `string` | `""` | Extra class names appended to the root wrapper. The track, label row, and footer keep their own classes and are not affected. |
34
+
35
+ <!-- prettier-ignore-end -->
36
+
37
+ ## Usage
38
+
39
+ ```tsx
40
+ <LinearProgress progress={40} status="Loading" labelTitle="Uploading report" />
41
+ ```
42
+
43
+ ```tsx
44
+ <LinearProgress progress="Unknown" indeterminate status="Loading" />
45
+ ```
46
+
47
+
48
+ ## Notes
49
+
50
+ - Determinate and indeterminate modes; Loading, Success, Warning, and Danger states.
51
+ - **Simple API** — toggle `label`, `percentage`, and `fieldMessage`; set `progress` and `status`.
52
+ - **Composable API** — `labelSlot` and `fieldMessageSlot` replace the built-in regions.
53
+ - **Accessibility**: Most ARIA is automatic: built-in `label`/`fieldMessage` wire `aria-labelledby` / `aria-describedby`; the track exposes `role="progressbar"`, value attrs, `aria-busy`, and `aria-invalid`; status updates use a polite live region. Pass `aria-label`, `aria-labelledby`, or `aria-describedby` only when hiding the built-in label, using slots, or supplementing associations. Keep `fieldMessage` for indeterminate and error outcomes.