first commit
This commit is contained in:
876
docs/ui/react/react-ui/README.md
Normal file
876
docs/ui/react/react-ui/README.md
Normal file
@@ -0,0 +1,876 @@
|
||||
# @suaveplan/react-ui - React UI component library — opinionated providers, theme/pack plugin contracts, bundled translations, telemetry, shadcn-compatible registry generator.
|
||||
|
||||
## Summary
|
||||
|
||||
> Chassis for the Suaveplan React UI component library — opinionated
|
||||
> providers, theme/pack plugin contracts, bundled translations,
|
||||
> telemetry, and a shadcn-compatible registry generator.
|
||||
|
||||
This package ships the **chassis** for `@suaveplan/react-ui`. The 57+
|
||||
components land per-batch under Wave 3 (a-h) of spec change
|
||||
[`enterprise-react-ui-v2`](../../../docs/design/enterprise-react-ui-v2);
|
||||
this README covers what Wave 2 ships.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [accordion](./src/components/accordion/accordion.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - accordion.variants — `accordionVariants`, `accordio…
|
||||
- [accordion](./src/components/accordion/accordion.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - accordion.variants — `accordionVariants`, `accordio…
|
||||
- [accordion.variants](./src/components/accordion/accordion.variants.md) — `accordionVariants`, `accordionItemVariants`, `accordionHeaderVariants`, `accordionTriggerVariants`, and `accordionCont…
|
||||
- [alert](./src/components/alert/alert.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - alert.variants — `alertVariants` is the cva config……
|
||||
- [alert](./src/components/alert/alert.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - alert.variants — `alertVariants` is the cva config……
|
||||
- [alert.variants](./src/components/alert/alert.variants.md) — `alertVariants` is the cva config that powers the `<Alert>` chassis component. It exposes a `variant` axis with four ru…
|
||||
- [alert-dialog](./src/components/alert-dialog/alert-dialog.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - alert-dialog.variants — `alertDialogOverlayVariants…
|
||||
- [alert-dialog](./src/components/alert-dialog/alert-dialog.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - alert-dialog.variants — `alertDialogOverlayVariants…
|
||||
- [alert-dialog.variants](./src/components/alert-dialog/alert-dialog.variants.md) — `alertDialogOverlayVariants`, `alertDialogContentVariants`, `alertDialogHeaderVariants`, `alertDialogFooterVariants`, `…
|
||||
- [aspect-ratio](./src/components/aspect-ratio/aspect-ratio.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - aspect-ratio.variants — `aspectRatioVariants` is th…
|
||||
- [aspect-ratio](./src/components/aspect-ratio/aspect-ratio.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - aspect-ratio.variants — `aspectRatioVariants` is th…
|
||||
- [aspect-ratio.variants](./src/components/aspect-ratio/aspect-ratio.variants.md) — `aspectRatioVariants` is the cva (class-variance-authority) configuration powering the `<AspectRatio>` outer wrapper. I…
|
||||
- [avatar](./src/components/avatar/avatar.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - avatar.variants — `avatarVariants` is the cva confi…
|
||||
- [avatar](./src/components/avatar/avatar.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - avatar.variants — `avatarVariants` is the cva confi…
|
||||
- [avatar.variants](./src/components/avatar/avatar.variants.md) — `avatarVariants` is the cva config that powers the `<Avatar>` chassis component (`<AvatarImage>` and `<AvatarFallback>`…
|
||||
- [badge](./src/components/badge/badge.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - badge.variants — `badgeVariants` is the cva config……
|
||||
- [badge](./src/components/badge/badge.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - badge.variants — `badgeVariants` is the cva config……
|
||||
- [badge.variants](./src/components/badge/badge.variants.md) — `badgeVariants` is the cva config that powers the `<Badge>` chassis component. It exposes a `variant` axis with four ru…
|
||||
- [breadcrumb](./src/components/breadcrumb/breadcrumb.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - breadcrumb.variants — `breadcrumbVariants`, `breadc…
|
||||
- [breadcrumb](./src/components/breadcrumb/breadcrumb.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - breadcrumb.variants — `breadcrumbVariants`, `breadc…
|
||||
- [breadcrumb.variants](./src/components/breadcrumb/breadcrumb.variants.md) — `breadcrumbVariants`, `breadcrumbListVariants`, `breadcrumbItemVariants`, `breadcrumbLinkVariants`, `breadcrumbPageVari…
|
||||
- [button](./src/components/button/button.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - button.variants — `buttonVariants` is the cva (clas…
|
||||
- [button](./src/components/button/button.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - button.variants — `buttonVariants` is the cva (clas…
|
||||
- [button.variants](./src/components/button/button.variants.md) — `buttonVariants` is the cva (class-variance-authority) configuration that powers every `<Button>` render. It is split f…
|
||||
- [calendar](./src/components/calendar/calendar.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - calendar.test-fixtures — Wave-3h local test fixture…
|
||||
- [calendar](./src/components/calendar/calendar.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - calendar.test-fixtures — Wave-3h local test fixture…
|
||||
- [calendar.test-fixtures](./src/components/calendar/calendar.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.calendar.*` keyset. The chas…
|
||||
- [calendar.variants](./src/components/calendar/calendar.variants.md) — `calendarVariants` is the cva config that powers the outer wrapper of `<Calendar>`. The recipe is intentionally minimal…
|
||||
- [card](./src/components/card/card.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - card.variants — `cardVariants` (and its sub-compone…
|
||||
- [card](./src/components/card/card.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - card.variants — `cardVariants` (and its sub-compone…
|
||||
- [card.variants](./src/components/card/card.variants.md) — `cardVariants` (and its sub-component cva resolvers `cardHeaderVariants`, `cardTitleVariants`, `cardDescriptionVariants…
|
||||
- [checkbox](./src/components/checkbox/checkbox.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - checkbox.variants — `checkboxVariants` is the cva (…
|
||||
- [checkbox](./src/components/checkbox/checkbox.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - checkbox.variants — `checkboxVariants` is the cva (…
|
||||
- [checkbox.variants](./src/components/checkbox/checkbox.variants.md) — `checkboxVariants` is the cva (class-variance-authority) configuration that powers every `<Checkbox>` render. It is spl…
|
||||
- [collapsible](./src/components/collapsible/collapsible.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - collapsible.variants — `collapsibleVariants`, `coll…
|
||||
- [collapsible](./src/components/collapsible/collapsible.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - collapsible.variants — `collapsibleVariants`, `coll…
|
||||
- [collapsible.variants](./src/components/collapsible/collapsible.variants.md) — `collapsibleVariants`, `collapsibleTriggerVariants`, and `collapsibleContentVariants` are the cva (class-variance-autho…
|
||||
- [confirm-delete-dialog](./src/components/confirm-delete-dialog/confirm-delete-dialog.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - confirm-delete-dialog.test-fixtures — Wave-3g local…
|
||||
- [confirm-delete-dialog](./src/components/confirm-delete-dialog/confirm-delete-dialog.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - confirm-delete-dialog.test-fixtures — Wave-3g local…
|
||||
- [confirm-delete-dialog.test-fixtures](./src/components/confirm-delete-dialog/confirm-delete-dialog.test-fixtures.md) — Wave-3g local test fixture that augments the chassis test i18n instance with the `react-ui.confirmDelete.*` keyset. The…
|
||||
- [context-menu](./src/components/context-menu/context-menu.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - context-menu.variants — `contextMenuContentVariants…
|
||||
- [context-menu](./src/components/context-menu/context-menu.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - context-menu.variants — `contextMenuContentVariants…
|
||||
- [context-menu.variants](./src/components/context-menu/context-menu.variants.md) — `contextMenuContentVariants`, `contextMenuItemVariants`, `contextMenuLabelVariants`, `contextMenuSeparatorVariants`, `c…
|
||||
- [data-table](./src/components/data-table/data-table.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - data-table.variants — `dataTableVariants`, `dataTab…
|
||||
- [data-table](./src/components/data-table/data-table.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - data-table.variants — `dataTableVariants`, `dataTab…
|
||||
- [data-table.variants](./src/components/data-table/data-table.variants.md) — `dataTableVariants`, `dataTablePaginationVariants`, and `dataTableSortHeaderVariants` drive the chassis `<DataTable>` o…
|
||||
- [dialog](./src/components/dialog/dialog.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - dialog.variants — `dialogOverlayVariants`, `dialogC…
|
||||
- [dialog](./src/components/dialog/dialog.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - dialog.variants — `dialogOverlayVariants`, `dialogC…
|
||||
- [dialog.variants](./src/components/dialog/dialog.variants.md) — `dialogOverlayVariants`, `dialogContentVariants`, `dialogHeaderVariants`, `dialogFooterVariants`, `dialogTitleVariants`…
|
||||
- [drawer](./src/components/drawer/drawer.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - drawer.variants — `drawerOverlayVariants`, `drawerC…
|
||||
- [drawer](./src/components/drawer/drawer.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - drawer.variants — `drawerOverlayVariants`, `drawerC…
|
||||
- [drawer.variants](./src/components/drawer/drawer.variants.md) — `drawerOverlayVariants`, `drawerContentVariants`, `drawerHandleVariants`, `drawerHeaderVariants`, `drawerFooterVariants…
|
||||
- [dropdown-menu](./src/components/dropdown-menu/dropdown-menu.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - dropdown-menu.variants — `dropdownMenuContentVarian…
|
||||
- [dropdown-menu](./src/components/dropdown-menu/dropdown-menu.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - dropdown-menu.variants — `dropdownMenuContentVarian…
|
||||
- [dropdown-menu.variants](./src/components/dropdown-menu/dropdown-menu.variants.md) — `dropdownMenuContentVariants`, `dropdownMenuItemVariants`, `dropdownMenuLabelVariants`, `dropdownMenuSeparatorVariants`…
|
||||
- [empty](./src/components/empty/empty.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - empty.variants — `emptyVariants` is the cva config……
|
||||
- [empty](./src/components/empty/empty.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - empty.variants — `emptyVariants` is the cva config……
|
||||
- [empty.variants](./src/components/empty/empty.variants.md) — `emptyVariants` is the cva config that powers the `<Empty>` chassis component (with `emptyHeaderVariants`, `emptyMediaV…
|
||||
- [error-boundary](./src/components/error-boundary/error-boundary.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - error-boundary.metrics — Co-located OTel counter fo…
|
||||
- [error-boundary](./src/components/error-boundary/error-boundary.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - error-boundary.metrics — Co-located OTel counter fo…
|
||||
- [error-boundary.metrics](./src/components/error-boundary/error-boundary.metrics.md) — Co-located OTel counter for the chassis `<ErrorBoundary>` component. Lives next to the boundary itself rather than in t…
|
||||
- [error-boundary.test-fixtures](./src/components/error-boundary/error-boundary.test-fixtures.md) — Wave-3g local test fixture that augments the chassis test i18n instance with the `react-ui.errorBoundary.*` keyset. The…
|
||||
- [error-boundary.variants](./src/components/error-boundary/error-boundary.variants.md) — Cva slot definitions for the chassis `<ErrorBoundary>` default fallback. Splits the fallback into four named slots — ou…
|
||||
- [field](./src/components/field/field.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - field.variants — `fieldVariants`, `fieldLabelVarian…
|
||||
- [field](./src/components/field/field.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - field.variants — `fieldVariants`, `fieldLabelVarian…
|
||||
- [field.variants](./src/components/field/field.variants.md) — `fieldVariants`, `fieldLabelVariants`, `fieldDescriptionVariants`, and `fieldErrorVariants` are the four cva (class-var…
|
||||
- [field-group](./src/components/field-group/field-group.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - field-group.variants — `fieldGroupVariants` is the……
|
||||
- [field-group](./src/components/field-group/field-group.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - field-group.variants — `fieldGroupVariants` is the……
|
||||
- [field-group.variants](./src/components/field-group/field-group.variants.md) — `fieldGroupVariants` is the cva (class-variance-authority) configuration powering every `<FieldGroup>` render. It is sp…
|
||||
- [field-set](./src/components/field-set/field-set.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - field-set.variants — `fieldSetVariants` and `fieldL…
|
||||
- [field-set](./src/components/field-set/field-set.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - field-set.variants — `fieldSetVariants` and `fieldL…
|
||||
- [field-set.variants](./src/components/field-set/field-set.variants.md) — `fieldSetVariants` and `fieldLegendVariants` are the cva (class-variance-authority) configurations powering `<FieldSet>…
|
||||
- [hover-card](./src/components/hover-card/hover-card.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - hover-card.variants — `hoverCardContentVariants` is…
|
||||
- [hover-card](./src/components/hover-card/hover-card.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - hover-card.variants — `hoverCardContentVariants` is…
|
||||
- [hover-card.variants](./src/components/hover-card/hover-card.variants.md) — `hoverCardContentVariants` is the cva configuration powering the `<HoverCardContent>` floating panel. It is split from…
|
||||
- [input](./src/components/input/input.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->- input-number — <!-- begin:auto-docs --> _(no submodu…
|
||||
- [input-number](./src/components/input/input-number/input-number.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [input-number](./src/components/input/input-number/input-number.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [input](./src/components/input/input.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->- input-number — <!-- begin:auto-docs --> _(no submodu…
|
||||
- [input.variants](./src/components/input/input.variants.md) — `inputVariants` is the cva (class-variance-authority) configuration that powers every `<Input>` render. It is split fro…
|
||||
- [input-group](./src/components/input-group/input-group.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - input-group.variants — `inputGroupVariants`, `input…
|
||||
- [input-group](./src/components/input-group/input-group.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - input-group.variants — `inputGroupVariants`, `input…
|
||||
- [input-group.variants](./src/components/input-group/input-group.variants.md) — `inputGroupVariants`, `inputGroupAddonVariants`, `inputGroupInputVariants`, `inputGroupTextareaVariants`, and `inputGro…
|
||||
- [label](./src/components/label/label.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - label.variants — `labelVariants` is the cva (class-…
|
||||
- [label](./src/components/label/label.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - label.variants — `labelVariants` is the cva (class-…
|
||||
- [label.variants](./src/components/label/label.variants.md) — `labelVariants` is the cva (class-variance-authority) configuration that powers every `<Label>` render. It is split fro…
|
||||
- [menubar](./src/components/menubar/menubar.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - menubar.variants — `menubarVariants`, `menubarTrigg…
|
||||
- [menubar](./src/components/menubar/menubar.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - menubar.variants — `menubarVariants`, `menubarTrigg…
|
||||
- [menubar.variants](./src/components/menubar/menubar.variants.md) — `menubarVariants`, `menubarTriggerVariants`, `menubarContentVariants`, `menubarItemVariants`, `menubarLabelVariants`, `…
|
||||
- [pagination](./src/components/pagination/pagination.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - pagination.variants — `paginationVariants`, `pagina…
|
||||
- [pagination](./src/components/pagination/pagination.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - pagination.variants — `paginationVariants`, `pagina…
|
||||
- [pagination.variants](./src/components/pagination/pagination.variants.md) — `paginationVariants`, `paginationContentVariants`, `paginationItemVariants`, `paginationLinkVariants`, `paginationPrevN…
|
||||
- [popover](./src/components/popover/popover.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - popover.variants — `popoverContentVariants` is the……
|
||||
- [popover](./src/components/popover/popover.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - popover.variants — `popoverContentVariants` is the……
|
||||
- [popover.variants](./src/components/popover/popover.variants.md) — `popoverContentVariants` is the cva configuration powering the `<PopoverContent>` floating panel. It is split from `pop…
|
||||
- [progress](./src/components/progress/progress.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - progress.variants — `progressVariants` is the cva c…
|
||||
- [progress](./src/components/progress/progress.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - progress.variants — `progressVariants` is the cva c…
|
||||
- [progress.variants](./src/components/progress/progress.variants.md) — `progressVariants` is the cva config that powers the `<Progress>` chassis component (paired with `progressIndicatorVari…
|
||||
- [radio-group](./src/components/radio-group/radio-group.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - radio-group.variants — `radioGroupVariants` and `ra…
|
||||
- [radio-group](./src/components/radio-group/radio-group.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - radio-group.variants — `radioGroupVariants` and `ra…
|
||||
- [radio-group.variants](./src/components/radio-group/radio-group.variants.md) — `radioGroupVariants` and `radioGroupItemVariants` are the cva (class-variance-authority) configurations powering `<Radi…
|
||||
- [resizable](./src/components/resizable/resizable.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - resizable.variants — `resizablePanelGroupVariants`,…
|
||||
- [resizable](./src/components/resizable/resizable.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - resizable.variants — `resizablePanelGroupVariants`,…
|
||||
- [resizable.variants](./src/components/resizable/resizable.variants.md) — `resizablePanelGroupVariants`, `resizablePanelVariants`, `resizableHandleVariants`, and `resizableHandleGripVariants` a…
|
||||
- [scroll-area](./src/components/scroll-area/scroll-area.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - scroll-area.variants — `scrollAreaVariants`, `scrol…
|
||||
- [scroll-area](./src/components/scroll-area/scroll-area.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - scroll-area.variants — `scrollAreaVariants`, `scrol…
|
||||
- [scroll-area.variants](./src/components/scroll-area/scroll-area.variants.md) — `scrollAreaVariants`, `scrollAreaViewportVariants`, `scrollAreaScrollbarVariants`, `scrollAreaThumbVariants`, and `scro…
|
||||
- [select](./src/components/select/select.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - select.test-fixtures — Wave-3h local test fixture t…
|
||||
- [select](./src/components/select/select.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - select.test-fixtures — Wave-3h local test fixture t…
|
||||
- [select.test-fixtures](./src/components/select/select.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.select.placeholder` key. The…
|
||||
- [select.variants](./src/components/select/select.variants.md) — The `<Select>` component is composed of six visual slots — trigger, content panel, item row, group label, separator, an…
|
||||
- [separator](./src/components/separator/separator.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - separator.variants — `separatorVariants` is the cva…
|
||||
- [separator](./src/components/separator/separator.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - separator.variants — `separatorVariants` is the cva…
|
||||
- [separator.variants](./src/components/separator/separator.variants.md) — `separatorVariants` is the cva (class-variance-authority) configuration that powers every `<Separator>` render. It is s…
|
||||
- [sheet](./src/components/sheet/sheet.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - sheet.variants — `sheetOverlayVariants`, `sheetCont…
|
||||
- [sheet](./src/components/sheet/sheet.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - sheet.variants — `sheetOverlayVariants`, `sheetCont…
|
||||
- [sheet.variants](./src/components/sheet/sheet.variants.md) — `sheetOverlayVariants`, `sheetContentVariants`, `sheetHeaderVariants`, `sheetFooterVariants`, `sheetTitleVariants`, `sh…
|
||||
- [skeleton](./src/components/skeleton/skeleton.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - skeleton.variants — `skeletonVariants` is the cva (…
|
||||
- [skeleton](./src/components/skeleton/skeleton.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - skeleton.variants — `skeletonVariants` is the cva (…
|
||||
- [skeleton.variants](./src/components/skeleton/skeleton.variants.md) — `skeletonVariants` is the cva (class-variance-authority) configuration that powers every `<Skeleton>` render. It is spl…
|
||||
- [sonner](./src/components/sonner/sonner.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - sonner.variants — Defines the `sonnerToasterVariant…
|
||||
- [sonner](./src/components/sonner/sonner.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - sonner.variants — Defines the `sonnerToasterVariant…
|
||||
- [sonner.variants](./src/components/sonner/sonner.variants.md) — Defines the `sonnerToasterVariants` cva slot for the chassis `<Sonner>` toaster host. Sonner manages its own stacking,…
|
||||
- [spinner](./src/components/spinner/spinner.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - spinner.variants — `spinnerVariants` is the cva con…
|
||||
- [spinner](./src/components/spinner/spinner.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - spinner.variants — `spinnerVariants` is the cva con…
|
||||
- [spinner.variants](./src/components/spinner/spinner.variants.md) — `spinnerVariants` is the cva config that powers the `<Spinner>` chassis component. It exposes a `size` axis (`sm` / `de…
|
||||
- [status-badge](./src/components/status-badge/status-badge.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - status-badge.variants — `statusBadgeVariants` is th…
|
||||
- [status-badge](./src/components/status-badge/status-badge.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - status-badge.variants — `statusBadgeVariants` is th…
|
||||
- [status-badge.variants](./src/components/status-badge/status-badge.variants.md) — `statusBadgeVariants` is the cva config that powers the `<StatusBadge>` chassis component. It exposes a `status` axis w…
|
||||
- [switch](./src/components/switch/switch.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - switch.variants — `switchVariants` and `switchThumb…
|
||||
- [switch](./src/components/switch/switch.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - switch.variants — `switchVariants` and `switchThumb…
|
||||
- [switch.variants](./src/components/switch/switch.variants.md) — `switchVariants` and `switchThumbVariants` are the two cva (class-variance-authority) configurations that together powe…
|
||||
- [table](./src/components/table/table.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - table.variants — `tableVariants` and its eight sub-…
|
||||
- [table](./src/components/table/table.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - table.variants — `tableVariants` and its eight sub-…
|
||||
- [table.variants](./src/components/table/table.variants.md) — `tableVariants` and its eight sub-component cva resolvers (`tableHeaderVariants`, `tableBodyVariants`, `tableFooterVari…
|
||||
- [tabs](./src/components/tabs/tabs.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - tabs.variants — `tabsVariants`, `tabsListVariants`,…
|
||||
- [tabs](./src/components/tabs/tabs.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - tabs.variants — `tabsVariants`, `tabsListVariants`,…
|
||||
- [tabs.variants](./src/components/tabs/tabs.variants.md) — `tabsVariants`, `tabsListVariants`, `tabsTriggerVariants`, and `tabsContentVariants` are the cva (class-variance-author…
|
||||
- [textarea](./src/components/textarea/textarea.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - textarea.variants — `textareaVariants` is the cva (…
|
||||
- [textarea](./src/components/textarea/textarea.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - textarea.variants — `textareaVariants` is the cva (…
|
||||
- [textarea.variants](./src/components/textarea/textarea.variants.md) — `textareaVariants` is the cva (class-variance-authority) configuration that powers every `<Textarea>` render. It is spl…
|
||||
- [theme-toggle](./src/components/theme-toggle/theme-toggle.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - theme-toggle.variants — Cva slot for the chassis `<…
|
||||
- [theme-toggle](./src/components/theme-toggle/theme-toggle.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - theme-toggle.variants — Cva slot for the chassis `<…
|
||||
- [theme-toggle.variants](./src/components/theme-toggle/theme-toggle.variants.md) — Cva slot for the chassis `<ThemeToggle>` icon-button. Mirrors the shape of `<Button variant="outline" size="icon">` so…
|
||||
- [toast](./src/components/toast/toast.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [toast](./src/components/toast/toast.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [toggle](./src/components/toggle/toggle.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - toggle.variants — `toggleVariants` is the cva (clas…
|
||||
- [toggle](./src/components/toggle/toggle.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - toggle.variants — `toggleVariants` is the cva (clas…
|
||||
- [toggle.variants](./src/components/toggle/toggle.variants.md) — `toggleVariants` is the cva (class-variance-authority) configuration powering every `<Toggle>` render. It is split from…
|
||||
- [toggle-group](./src/components/toggle-group/toggle-group.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - toggle-group.variants — `toggleGroupVariants` and `…
|
||||
- [toggle-group](./src/components/toggle-group/toggle-group.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - toggle-group.variants — `toggleGroupVariants` and `…
|
||||
- [toggle-group.variants](./src/components/toggle-group/toggle-group.variants.md) — `toggleGroupVariants` and `toggleGroupItemVariants` are the cva (class-variance-authority) configurations powering `<To…
|
||||
- [tooltip](./src/components/tooltip/tooltip.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - tooltip.variants — `tooltipContentVariants` is the……
|
||||
- [tooltip](./src/components/tooltip/tooltip.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - tooltip.variants — `tooltipContentVariants` is the……
|
||||
- [tooltip.variants](./src/components/tooltip/tooltip.variants.md) — `tooltipContentVariants` is the cva configuration powering the `<TooltipContent>` floating bubble. It is split from `to…
|
||||
- [use-toast-sonner-bridge](./src/components/use-toast-sonner-bridge/use-toast-sonner-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [use-toast-sonner-bridge](./src/components/use-toast-sonner-bridge/use-toast-sonner-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [error-codes](./src/errors/error-codes.md) — Defines every stable error code emitted by the `@suaveplan/react-ui` package. Codes are grouped under the `REACT_UI` na…
|
||||
- [react-ui-error](./src/errors/react-ui-error.md) — Owns every error thrown by the `@suaveplan/react-ui` package. Extends the project-wide `SuaveplanError` so each instan…
|
||||
- [use-mobile](./src/hooks/use-mobile/use-mobile.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [use-mobile](./src/hooks/use-mobile/use-mobile.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [use-toast](./src/hooks/use-toast/use-toast.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [use-toast](./src/hooks/use-toast/use-toast.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [formatted-cell](./src/i18n/formatted-cell/formatted-cell.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [formatted-cell](./src/i18n/formatted-cell/formatted-cell.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [formatted-currency](./src/i18n/formatted-currency/formatted-currency.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [formatted-currency](./src/i18n/formatted-currency/formatted-currency.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [formatted-date](./src/i18n/formatted-date/formatted-date.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [formatted-date](./src/i18n/formatted-date/formatted-date.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [formatted-list](./src/i18n/formatted-list/formatted-list.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [formatted-list](./src/i18n/formatted-list/formatted-list.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [formatted-message](./src/i18n/formatted-message/formatted-message.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [formatted-message](./src/i18n/formatted-message/formatted-message.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [formatted-number](./src/i18n/formatted-number/formatted-number.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [formatted-number](./src/i18n/formatted-number/formatted-number.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [formatted-relative-time](./src/i18n/formatted-relative-time/formatted-relative-time.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [formatted-relative-time](./src/i18n/formatted-relative-time/formatted-relative-time.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [plural](./src/i18n/plural/plural.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [plural](./src/i18n/plural/plural.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [react-ui-i18n-bridge](./src/i18n/react-ui-i18n-bridge/react-ui-i18n-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [react-ui-i18n-bridge](./src/i18n/react-ui-i18n-bridge/react-ui-i18n-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [stories-shell](./src/i18n/stories-shell/stories-shell.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [stories-shell](./src/i18n/stories-shell/stories-shell.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [tanstack-bridge](./src/i18n/tanstack-bridge/tanstack-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [tanstack-bridge](./src/i18n/tanstack-bridge/tanstack-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [validation-message-bridge](./src/i18n/validation-message-bridge/validation-message-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [validation-message-bridge](./src/i18n/validation-message-bridge/validation-message-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [augmentation](./src/locales/augmentation.md) — Side-effect-only TypeScript module that augments `@suaveplan/react-i18n`'s `ContributedTranslationKeys` interface with…
|
||||
- [schema](./src/locales/schema.md) — Zod schema asserting that every locale bundle declares exactly the keys present in the canonical `en-GB.json` source-of…
|
||||
- [translation-keys](./src/locales/translation-keys.md) — `translation-keys.ts` is the codegen-emitted string-literal union of every translation key contributed by `@suaveplan/r…
|
||||
- [observability](./src/observability/react-ui-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [react-ui-metrics](./src/observability/react-ui-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [pack](./src/pack/pack-context.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->- pack-registry-provider — <!-- begin:auto-docs --> _(…
|
||||
- [pack-registry-provider](./src/pack/pack-registry-provider/pack-registry-provider.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [pack-registry-provider](./src/pack/pack-registry-provider/pack-registry-provider.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [runtime-override](./src/pack/runtime-override/runtime-override.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [runtime-override](./src/pack/runtime-override/runtime-override.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [use-pack-component](./src/pack/use-pack-component/use-pack-component.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [use-pack-component](./src/pack/use-pack-component/use-pack-component.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [version-check](./src/pack/version-check/version-check.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [version-check](./src/pack/version-check/version-check.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [pack-context](./src/pack/pack-context.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->- pack-registry-provider — <!-- begin:auto-docs --> _(…
|
||||
- [react-ui-provider](./src/provider/react-ui-provider/react-ui-provider.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [react-ui-provider](./src/provider/react-ui-provider/react-ui-provider.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [theme](./src/theme/theme-context.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->- theme-provider — <!-- begin:auto-docs --> _(no submo…
|
||||
- [layer-stack](./src/theme/layer-stack/layer-stack.md) — The `layer-stack` module is the runtime engine that backs `<ThemeProvider>`, `useThemeMutator()`, and `useResolvedTheme…
|
||||
- [layer-stack](./src/theme/layer-stack/layer-stack.md) — The `layer-stack` module is the runtime engine that backs `<ThemeProvider>`, `useThemeMutator()`, and `useResolvedTheme…
|
||||
- [theme-provider](./src/theme/theme-provider/theme-provider.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [theme-provider](./src/theme/theme-provider/theme-provider.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [theme-switcher](./src/theme/theme-switcher/theme-switcher.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [theme-switcher](./src/theme/theme-switcher/theme-switcher.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [dark](./src/theme/themes/dark.md) — The dark counterpart of `lightTheme`. Same variant surface, same token names, lightness anchors inverted so foreground…
|
||||
- [high-contrast](./src/theme/themes/high-contrast.md) — Accessibility-first theme that meets WCAG AAA contrast for normal and large text alike. Pure-white foreground (`#FFFFFF…
|
||||
- [light](./src/theme/themes/light.md) — The default `ThemePlugin` instance shipped with `@suaveplan/react-ui`. `lightTheme` matches the shadcn `nova` token pre…
|
||||
- [use-resolved-theme-tokens](./src/theme/use-resolved-theme-tokens/use-resolved-theme-tokens.md) — `useResolvedThemeTokens()` is the read-side counterpart to `useThemeMutator()`. It returns the live merged token state…
|
||||
- [use-resolved-theme-tokens](./src/theme/use-resolved-theme-tokens/use-resolved-theme-tokens.md) — `useResolvedThemeTokens()` is the read-side counterpart to `useThemeMutator()`. It returns the live merged token state…
|
||||
- [use-theme](./src/theme/use-theme/use-theme.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [use-theme](./src/theme/use-theme/use-theme.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [use-theme-mutator](./src/theme/use-theme-mutator/use-theme-mutator.md) — `useThemeMutator()` is the public surface a consumer reaches for when it wants to push CSS custom properties onto the a…
|
||||
- [use-theme-mutator](./src/theme/use-theme-mutator/use-theme-mutator.md) — `useThemeMutator()` is the public surface a consumer reaches for when it wants to push CSS custom properties onto the a…
|
||||
- [use-theme-tokens](./src/theme/use-theme-tokens/use-theme-tokens.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [use-theme-tokens](./src/theme/use-theme-tokens/use-theme-tokens.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [use-themed-variants](./src/theme/use-themed-variants/use-themed-variants.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [use-themed-variants](./src/theme/use-themed-variants/use-themed-variants.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [theme-context](./src/theme/theme-context.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->- theme-provider — <!-- begin:auto-docs --> _(no submo…
|
||||
- [cn](./src/utils/cn/cn.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [cn](./src/utils/cn/cn.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [motion](./src/utils/motion/motion.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [motion](./src/utils/motion/motion.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [variants](./src/variants/variants.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [variants](./src/variants/variants.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [version](./src/version.md) — Declares the running version of `@suaveplan/react-ui` as the constant `REACT_UI_VERSION`. The version is hand-maintaine…
|
||||
<!-- end:auto-docs --> - accordion.variants — `accordionVariants`, `accordio…
|
||||
- [accordion](./src/components/accordion/accordion.md) — <!-- begin:auto-docs --> _(no submodules)_- accordion.variants — `accordionVariants`, `accordio…
|
||||
- [accordion.variants](./src/components/accordion/accordion.variants.md) — `accordionVariants`, `accordionItemVariants`, `accordionHeaderVariants`, `accordionTriggerVariants`, and `accordionCont…
|
||||
- [alert](./src/components/alert/alert.md) — <!-- begin:auto-docs --> _(no submodules)_- alert.variants — `alertVariants` is the cva config……
|
||||
- [alert](./src/components/alert/alert.md) — <!-- begin:auto-docs --> _(no submodules)_- alert.variants — `alertVariants` is the cva config……
|
||||
- [alert.variants](./src/components/alert/alert.variants.md) — `alertVariants` is the cva config that powers the `<Alert>` chassis component. It exposes a `variant` axis with four ru…
|
||||
- [alert-dialog](./src/components/alert-dialog/alert-dialog.md) — <!-- begin:auto-docs --> _(no submodules)_- alert-dialog.variants — `alertDialogOverlayVariants…
|
||||
- [alert-dialog](./src/components/alert-dialog/alert-dialog.md) — <!-- begin:auto-docs --> _(no submodules)_- alert-dialog.variants — `alertDialogOverlayVariants…
|
||||
- [alert-dialog.variants](./src/components/alert-dialog/alert-dialog.variants.md) — `alertDialogOverlayVariants`, `alertDialogContentVariants`, `alertDialogHeaderVariants`, `alertDialogFooterVariants`, `…
|
||||
- [aspect-ratio](./src/components/aspect-ratio/aspect-ratio.md) — <!-- begin:auto-docs --> _(no submodules)_- aspect-ratio.variants — `aspectRatioVariants` is th…
|
||||
- [aspect-ratio](./src/components/aspect-ratio/aspect-ratio.md) — <!-- begin:auto-docs --> _(no submodules)_- aspect-ratio.variants — `aspectRatioVariants` is th…
|
||||
- [aspect-ratio.variants](./src/components/aspect-ratio/aspect-ratio.variants.md) — `aspectRatioVariants` is the cva (class-variance-authority) configuration powering the `<AspectRatio>` outer wrapper. I…
|
||||
- [avatar](./src/components/avatar/avatar.md) — <!-- begin:auto-docs --> _(no submodules)_- avatar.variants — `avatarVariants` is the cva confi…
|
||||
- [avatar](./src/components/avatar/avatar.md) — <!-- begin:auto-docs --> _(no submodules)_- avatar.variants — `avatarVariants` is the cva confi…
|
||||
- [avatar.variants](./src/components/avatar/avatar.variants.md) — `avatarVariants` is the cva config that powers the `<Avatar>` chassis component (`<AvatarImage>` and `<AvatarFallback>`…
|
||||
- [badge](./src/components/badge/badge.md) — <!-- begin:auto-docs --> _(no submodules)_- badge.variants — `badgeVariants` is the cva config……
|
||||
- [badge](./src/components/badge/badge.md) — <!-- begin:auto-docs --> _(no submodules)_- badge.variants — `badgeVariants` is the cva config……
|
||||
- [badge.variants](./src/components/badge/badge.variants.md) — `badgeVariants` is the cva config that powers the `<Badge>` chassis component. It exposes a `variant` axis with four ru…
|
||||
- [breadcrumb](./src/components/breadcrumb/breadcrumb.md) — <!-- begin:auto-docs --> _(no submodules)_- breadcrumb.variants — `breadcrumbVariants`, `breadc…
|
||||
- [breadcrumb](./src/components/breadcrumb/breadcrumb.md) — <!-- begin:auto-docs --> _(no submodules)_- breadcrumb.variants — `breadcrumbVariants`, `breadc…
|
||||
- [breadcrumb.variants](./src/components/breadcrumb/breadcrumb.variants.md) — `breadcrumbVariants`, `breadcrumbListVariants`, `breadcrumbItemVariants`, `breadcrumbLinkVariants`, `breadcrumbPageVari…
|
||||
- [button](./src/components/button/button.md) — <!-- begin:auto-docs --> _(no submodules)_- button.variants — `buttonVariants` is the cva (clas…
|
||||
- [button](./src/components/button/button.md) — <!-- begin:auto-docs --> _(no submodules)_- button.variants — `buttonVariants` is the cva (clas…
|
||||
- [button.variants](./src/components/button/button.variants.md) — `buttonVariants` is the cva (class-variance-authority) configuration that powers every `<Button>` render. It is split f…
|
||||
- [calendar](./src/components/calendar/calendar.md) — <!-- begin:auto-docs --> _(no submodules)_- calendar.test-fixtures — Wave-3h local test fixture…
|
||||
- [calendar](./src/components/calendar/calendar.md) — <!-- begin:auto-docs --> _(no submodules)_- calendar.test-fixtures — Wave-3h local test fixture…
|
||||
- [calendar.test-fixtures](./src/components/calendar/calendar.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.calendar.*` keyset. The chas…
|
||||
- [calendar.variants](./src/components/calendar/calendar.variants.md) — `calendarVariants` is the cva config that powers the outer wrapper of `<Calendar>`. The recipe is intentionally minimal…
|
||||
- [card](./src/components/card/card.md) — <!-- begin:auto-docs --> _(no submodules)_- card.variants — `cardVariants` (and its sub-compone…
|
||||
- [card](./src/components/card/card.md) — <!-- begin:auto-docs --> _(no submodules)_- card.variants — `cardVariants` (and its sub-compone…
|
||||
- [card.variants](./src/components/card/card.variants.md) — `cardVariants` (and its sub-component cva resolvers `cardHeaderVariants`, `cardTitleVariants`, `cardDescriptionVariants…
|
||||
- [checkbox](./src/components/checkbox/checkbox.md) — <!-- begin:auto-docs --> _(no submodules)_- checkbox.variants — `checkboxVariants` is the cva (…
|
||||
- [checkbox](./src/components/checkbox/checkbox.md) — <!-- begin:auto-docs --> _(no submodules)_- checkbox.variants — `checkboxVariants` is the cva (…
|
||||
- [checkbox.variants](./src/components/checkbox/checkbox.variants.md) — `checkboxVariants` is the cva (class-variance-authority) configuration that powers every `<Checkbox>` render. It is spl…
|
||||
- [collapsible](./src/components/collapsible/collapsible.md) — <!-- begin:auto-docs --> _(no submodules)_- collapsible.variants — `collapsibleVariants`, `coll…
|
||||
- [collapsible](./src/components/collapsible/collapsible.md) — <!-- begin:auto-docs --> _(no submodules)_- collapsible.variants — `collapsibleVariants`, `coll…
|
||||
- [collapsible.variants](./src/components/collapsible/collapsible.variants.md) — `collapsibleVariants`, `collapsibleTriggerVariants`, and `collapsibleContentVariants` are the cva (class-variance-autho…
|
||||
- [confirm-delete-dialog](./src/components/confirm-delete-dialog/confirm-delete-dialog.md) — <!-- begin:auto-docs --> _(no submodules)_- confirm-delete-dialog.test-fixtures — Wave-3g local…
|
||||
- [confirm-delete-dialog](./src/components/confirm-delete-dialog/confirm-delete-dialog.md) — <!-- begin:auto-docs --> _(no submodules)_- confirm-delete-dialog.test-fixtures — Wave-3g local…
|
||||
- [confirm-delete-dialog.test-fixtures](./src/components/confirm-delete-dialog/confirm-delete-dialog.test-fixtures.md) — Wave-3g local test fixture that augments the chassis test i18n instance with the `react-ui.confirmDelete.*` keyset. The…
|
||||
- [context-menu](./src/components/context-menu/context-menu.md) — <!-- begin:auto-docs --> _(no submodules)_- context-menu.variants — `contextMenuContentVariants…
|
||||
- [context-menu](./src/components/context-menu/context-menu.md) — <!-- begin:auto-docs --> _(no submodules)_- context-menu.variants — `contextMenuContentVariants…
|
||||
- [context-menu.variants](./src/components/context-menu/context-menu.variants.md) — `contextMenuContentVariants`, `contextMenuItemVariants`, `contextMenuLabelVariants`, `contextMenuSeparatorVariants`, `c…
|
||||
- [data-table](./src/components/data-table/data-table.md) — <!-- begin:auto-docs --> _(no submodules)_- data-table.variants — `dataTableVariants`, `dataTab…
|
||||
- [data-table](./src/components/data-table/data-table.md) — <!-- begin:auto-docs --> _(no submodules)_- data-table.variants — `dataTableVariants`, `dataTab…
|
||||
- [data-table.variants](./src/components/data-table/data-table.variants.md) — `dataTableVariants`, `dataTablePaginationVariants`, and `dataTableSortHeaderVariants` drive the chassis `<DataTable>` o…
|
||||
- [dialog](./src/components/dialog/dialog.md) — <!-- begin:auto-docs --> _(no submodules)_- dialog.variants — `dialogOverlayVariants`, `dialogC…
|
||||
- [dialog](./src/components/dialog/dialog.md) — <!-- begin:auto-docs --> _(no submodules)_- dialog.variants — `dialogOverlayVariants`, `dialogC…
|
||||
- [dialog.variants](./src/components/dialog/dialog.variants.md) — `dialogOverlayVariants`, `dialogContentVariants`, `dialogHeaderVariants`, `dialogFooterVariants`, `dialogTitleVariants`…
|
||||
- [drawer](./src/components/drawer/drawer.md) — <!-- begin:auto-docs --> _(no submodules)_- drawer.variants — `drawerOverlayVariants`, `drawerC…
|
||||
- [drawer](./src/components/drawer/drawer.md) — <!-- begin:auto-docs --> _(no submodules)_- drawer.variants — `drawerOverlayVariants`, `drawerC…
|
||||
- [drawer.variants](./src/components/drawer/drawer.variants.md) — `drawerOverlayVariants`, `drawerContentVariants`, `drawerHandleVariants`, `drawerHeaderVariants`, `drawerFooterVariants…
|
||||
- [dropdown-menu](./src/components/dropdown-menu/dropdown-menu.md) — <!-- begin:auto-docs --> _(no submodules)_- dropdown-menu.variants — `dropdownMenuContentVarian…
|
||||
- [dropdown-menu](./src/components/dropdown-menu/dropdown-menu.md) — <!-- begin:auto-docs --> _(no submodules)_- dropdown-menu.variants — `dropdownMenuContentVarian…
|
||||
- [dropdown-menu.variants](./src/components/dropdown-menu/dropdown-menu.variants.md) — `dropdownMenuContentVariants`, `dropdownMenuItemVariants`, `dropdownMenuLabelVariants`, `dropdownMenuSeparatorVariants`…
|
||||
- [empty](./src/components/empty/empty.md) — <!-- begin:auto-docs --> _(no submodules)_- empty.variants — `emptyVariants` is the cva config……
|
||||
- [empty](./src/components/empty/empty.md) — <!-- begin:auto-docs --> _(no submodules)_- empty.variants — `emptyVariants` is the cva config……
|
||||
- [empty.variants](./src/components/empty/empty.variants.md) — `emptyVariants` is the cva config that powers the `<Empty>` chassis component (with `emptyHeaderVariants`, `emptyMediaV…
|
||||
- [error-boundary](./src/components/error-boundary/error-boundary.md) — <!-- begin:auto-docs --> _(no submodules)_- error-boundary.metrics — Co-located OTel counter fo…
|
||||
- [error-boundary](./src/components/error-boundary/error-boundary.md) — <!-- begin:auto-docs --> _(no submodules)_- error-boundary.metrics — Co-located OTel counter fo…
|
||||
- [error-boundary.metrics](./src/components/error-boundary/error-boundary.metrics.md) — Co-located OTel counter for the chassis `<ErrorBoundary>` component. Lives next to the boundary itself rather than in t…
|
||||
- [error-boundary.test-fixtures](./src/components/error-boundary/error-boundary.test-fixtures.md) — Wave-3g local test fixture that augments the chassis test i18n instance with the `react-ui.errorBoundary.*` keyset. The…
|
||||
- [error-boundary.variants](./src/components/error-boundary/error-boundary.variants.md) — Cva slot definitions for the chassis `<ErrorBoundary>` default fallback. Splits the fallback into four named slots — ou…
|
||||
- [field](./src/components/field/field.md) — <!-- begin:auto-docs --> _(no submodules)_- field.variants — `fieldVariants`, `fieldLabelVarian…
|
||||
- [field](./src/components/field/field.md) — <!-- begin:auto-docs --> _(no submodules)_- field.variants — `fieldVariants`, `fieldLabelVarian…
|
||||
- [field.variants](./src/components/field/field.variants.md) — `fieldVariants`, `fieldLabelVariants`, `fieldDescriptionVariants`, and `fieldErrorVariants` are the four cva (class-var…
|
||||
- [field-group](./src/components/field-group/field-group.md) — <!-- begin:auto-docs --> _(no submodules)_- field-group.variants — `fieldGroupVariants` is the……
|
||||
- [field-group](./src/components/field-group/field-group.md) — <!-- begin:auto-docs --> _(no submodules)_- field-group.variants — `fieldGroupVariants` is the……
|
||||
- [field-group.variants](./src/components/field-group/field-group.variants.md) — `fieldGroupVariants` is the cva (class-variance-authority) configuration powering every `<FieldGroup>` render. It is sp…
|
||||
- [field-set](./src/components/field-set/field-set.md) — <!-- begin:auto-docs --> _(no submodules)_- field-set.variants — `fieldSetVariants` and `fieldL…
|
||||
- [field-set](./src/components/field-set/field-set.md) — <!-- begin:auto-docs --> _(no submodules)_- field-set.variants — `fieldSetVariants` and `fieldL…
|
||||
- [field-set.variants](./src/components/field-set/field-set.variants.md) — `fieldSetVariants` and `fieldLegendVariants` are the cva (class-variance-authority) configurations powering `<FieldSet>…
|
||||
- [hover-card](./src/components/hover-card/hover-card.md) — <!-- begin:auto-docs --> _(no submodules)_- hover-card.variants — `hoverCardContentVariants` is…
|
||||
- [hover-card](./src/components/hover-card/hover-card.md) — <!-- begin:auto-docs --> _(no submodules)_- hover-card.variants — `hoverCardContentVariants` is…
|
||||
- [hover-card.variants](./src/components/hover-card/hover-card.variants.md) — `hoverCardContentVariants` is the cva configuration powering the `<HoverCardContent>` floating panel. It is split from…
|
||||
- [input](./src/components/input/input.md) — <!-- begin:auto-docs --> _(no submodules)_- [input-number](./src/components/input/input-number/input-number.md) — <!-- begin:auto-docs --> _(no submodules)_- [input-number](./src/components/input/input-number/input-number.md) — <!-- begin:auto-docs --> _(no submodules)_- [input](./src/components/input/input.md) — <!-- begin:auto-docs --> _(no submodules)_- [input.variants](./src/components/input/input.variants.md) — `inputVariants` is the cva (class-variance-authority) configuration that powers every `<Input>` render. It is split fro…
|
||||
- [input-group](./src/components/input-group/input-group.md) — <!-- begin:auto-docs --> _(no submodules)_- input-group.variants — `inputGroupVariants`, `input…
|
||||
- [input-group](./src/components/input-group/input-group.md) — <!-- begin:auto-docs --> _(no submodules)_- input-group.variants — `inputGroupVariants`, `input…
|
||||
- [input-group.variants](./src/components/input-group/input-group.variants.md) — `inputGroupVariants`, `inputGroupAddonVariants`, `inputGroupInputVariants`, `inputGroupTextareaVariants`, and `inputGro…
|
||||
- [label](./src/components/label/label.md) — <!-- begin:auto-docs --> _(no submodules)_- label.variants — `labelVariants` is the cva (class-…
|
||||
- [label](./src/components/label/label.md) — <!-- begin:auto-docs --> _(no submodules)_- label.variants — `labelVariants` is the cva (class-…
|
||||
- [label.variants](./src/components/label/label.variants.md) — `labelVariants` is the cva (class-variance-authority) configuration that powers every `<Label>` render. It is split fro…
|
||||
- [menubar](./src/components/menubar/menubar.md) — <!-- begin:auto-docs --> _(no submodules)_- menubar.variants — `menubarVariants`, `menubarTrigg…
|
||||
- [menubar](./src/components/menubar/menubar.md) — <!-- begin:auto-docs --> _(no submodules)_- menubar.variants — `menubarVariants`, `menubarTrigg…
|
||||
- [menubar.variants](./src/components/menubar/menubar.variants.md) — `menubarVariants`, `menubarTriggerVariants`, `menubarContentVariants`, `menubarItemVariants`, `menubarLabelVariants`, `…
|
||||
- [pagination](./src/components/pagination/pagination.md) — <!-- begin:auto-docs --> _(no submodules)_- pagination.variants — `paginationVariants`, `pagina…
|
||||
- [pagination](./src/components/pagination/pagination.md) — <!-- begin:auto-docs --> _(no submodules)_- pagination.variants — `paginationVariants`, `pagina…
|
||||
- [pagination.variants](./src/components/pagination/pagination.variants.md) — `paginationVariants`, `paginationContentVariants`, `paginationItemVariants`, `paginationLinkVariants`, `paginationPrevN…
|
||||
- [popover](./src/components/popover/popover.md) — <!-- begin:auto-docs --> _(no submodules)_- popover.variants — `popoverContentVariants` is the……
|
||||
- [popover](./src/components/popover/popover.md) — <!-- begin:auto-docs --> _(no submodules)_- popover.variants — `popoverContentVariants` is the……
|
||||
- [popover.variants](./src/components/popover/popover.variants.md) — `popoverContentVariants` is the cva configuration powering the `<PopoverContent>` floating panel. It is split from `pop…
|
||||
- [progress](./src/components/progress/progress.md) — <!-- begin:auto-docs --> _(no submodules)_- progress.variants — `progressVariants` is the cva c…
|
||||
- [progress](./src/components/progress/progress.md) — <!-- begin:auto-docs --> _(no submodules)_- progress.variants — `progressVariants` is the cva c…
|
||||
- [progress.variants](./src/components/progress/progress.variants.md) — `progressVariants` is the cva config that powers the `<Progress>` chassis component (paired with `progressIndicatorVari…
|
||||
- [radio-group](./src/components/radio-group/radio-group.md) — <!-- begin:auto-docs --> _(no submodules)_- radio-group.variants — `radioGroupVariants` and `ra…
|
||||
- [radio-group](./src/components/radio-group/radio-group.md) — <!-- begin:auto-docs --> _(no submodules)_- radio-group.variants — `radioGroupVariants` and `ra…
|
||||
- [radio-group.variants](./src/components/radio-group/radio-group.variants.md) — `radioGroupVariants` and `radioGroupItemVariants` are the cva (class-variance-authority) configurations powering `<Radi…
|
||||
- [resizable](./src/components/resizable/resizable.md) — <!-- begin:auto-docs --> _(no submodules)_- resizable.variants — `resizablePanelGroupVariants`,…
|
||||
- [resizable](./src/components/resizable/resizable.md) — <!-- begin:auto-docs --> _(no submodules)_- resizable.variants — `resizablePanelGroupVariants`,…
|
||||
- [resizable.variants](./src/components/resizable/resizable.variants.md) — `resizablePanelGroupVariants`, `resizablePanelVariants`, `resizableHandleVariants`, and `resizableHandleGripVariants` a…
|
||||
- [scroll-area](./src/components/scroll-area/scroll-area.md) — <!-- begin:auto-docs --> _(no submodules)_- scroll-area.variants — `scrollAreaVariants`, `scrol…
|
||||
- [scroll-area](./src/components/scroll-area/scroll-area.md) — <!-- begin:auto-docs --> _(no submodules)_- scroll-area.variants — `scrollAreaVariants`, `scrol…
|
||||
- [scroll-area.variants](./src/components/scroll-area/scroll-area.variants.md) — `scrollAreaVariants`, `scrollAreaViewportVariants`, `scrollAreaScrollbarVariants`, `scrollAreaThumbVariants`, and `scro…
|
||||
- [select](./src/components/select/select.md) — <!-- begin:auto-docs --> _(no submodules)_- select.test-fixtures — Wave-3h local test fixture t…
|
||||
- [select](./src/components/select/select.md) — <!-- begin:auto-docs --> _(no submodules)_- select.test-fixtures — Wave-3h local test fixture t…
|
||||
- [select.test-fixtures](./src/components/select/select.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.select.placeholder` key. The…
|
||||
- [select.variants](./src/components/select/select.variants.md) — The `<Select>` component is composed of six visual slots — trigger, content panel, item row, group label, separator, an…
|
||||
- [separator](./src/components/separator/separator.md) — <!-- begin:auto-docs --> _(no submodules)_- separator.variants — `separatorVariants` is the cva…
|
||||
- [separator](./src/components/separator/separator.md) — <!-- begin:auto-docs --> _(no submodules)_- separator.variants — `separatorVariants` is the cva…
|
||||
- [separator.variants](./src/components/separator/separator.variants.md) — `separatorVariants` is the cva (class-variance-authority) configuration that powers every `<Separator>` render. It is s…
|
||||
- [sheet](./src/components/sheet/sheet.md) — <!-- begin:auto-docs --> _(no submodules)_- sheet.variants — `sheetOverlayVariants`, `sheetCont…
|
||||
- [sheet](./src/components/sheet/sheet.md) — <!-- begin:auto-docs --> _(no submodules)_- sheet.variants — `sheetOverlayVariants`, `sheetCont…
|
||||
- [sheet.variants](./src/components/sheet/sheet.variants.md) — `sheetOverlayVariants`, `sheetContentVariants`, `sheetHeaderVariants`, `sheetFooterVariants`, `sheetTitleVariants`, `sh…
|
||||
- [skeleton](./src/components/skeleton/skeleton.md) — <!-- begin:auto-docs --> _(no submodules)_- skeleton.variants — `skeletonVariants` is the cva (…
|
||||
- [skeleton](./src/components/skeleton/skeleton.md) — <!-- begin:auto-docs --> _(no submodules)_- skeleton.variants — `skeletonVariants` is the cva (…
|
||||
- [skeleton.variants](./src/components/skeleton/skeleton.variants.md) — `skeletonVariants` is the cva (class-variance-authority) configuration that powers every `<Skeleton>` render. It is spl…
|
||||
- [sonner](./src/components/sonner/sonner.md) — <!-- begin:auto-docs --> _(no submodules)_- sonner.variants — Defines the `sonnerToasterVariant…
|
||||
- [sonner](./src/components/sonner/sonner.md) — <!-- begin:auto-docs --> _(no submodules)_- sonner.variants — Defines the `sonnerToasterVariant…
|
||||
- [sonner.variants](./src/components/sonner/sonner.variants.md) — Defines the `sonnerToasterVariants` cva slot for the chassis `<Sonner>` toaster host. Sonner manages its own stacking,…
|
||||
- [spinner](./src/components/spinner/spinner.md) — <!-- begin:auto-docs --> _(no submodules)_- spinner.variants — `spinnerVariants` is the cva con…
|
||||
- [spinner](./src/components/spinner/spinner.md) — <!-- begin:auto-docs --> _(no submodules)_- spinner.variants — `spinnerVariants` is the cva con…
|
||||
- [spinner.variants](./src/components/spinner/spinner.variants.md) — `spinnerVariants` is the cva config that powers the `<Spinner>` chassis component. It exposes a `size` axis (`sm` / `de…
|
||||
- [status-badge](./src/components/status-badge/status-badge.md) — <!-- begin:auto-docs --> _(no submodules)_- status-badge.variants — `statusBadgeVariants` is th…
|
||||
- [status-badge](./src/components/status-badge/status-badge.md) — <!-- begin:auto-docs --> _(no submodules)_- status-badge.variants — `statusBadgeVariants` is th…
|
||||
- [status-badge.variants](./src/components/status-badge/status-badge.variants.md) — `statusBadgeVariants` is the cva config that powers the `<StatusBadge>` chassis component. It exposes a `status` axis w…
|
||||
- [switch](./src/components/switch/switch.md) — <!-- begin:auto-docs --> _(no submodules)_- switch.variants — `switchVariants` and `switchThumb…
|
||||
- [switch](./src/components/switch/switch.md) — <!-- begin:auto-docs --> _(no submodules)_- switch.variants — `switchVariants` and `switchThumb…
|
||||
- [switch.variants](./src/components/switch/switch.variants.md) — `switchVariants` and `switchThumbVariants` are the two cva (class-variance-authority) configurations that together powe…
|
||||
- [table](./src/components/table/table.md) — <!-- begin:auto-docs --> _(no submodules)_- table.variants — `tableVariants` and its eight sub-…
|
||||
- [table](./src/components/table/table.md) — <!-- begin:auto-docs --> _(no submodules)_- table.variants — `tableVariants` and its eight sub-…
|
||||
- [table.variants](./src/components/table/table.variants.md) — `tableVariants` and its eight sub-component cva resolvers (`tableHeaderVariants`, `tableBodyVariants`, `tableFooterVari…
|
||||
- [tabs](./src/components/tabs/tabs.md) — <!-- begin:auto-docs --> _(no submodules)_- tabs.variants — `tabsVariants`, `tabsListVariants`,…
|
||||
- [tabs](./src/components/tabs/tabs.md) — <!-- begin:auto-docs --> _(no submodules)_- tabs.variants — `tabsVariants`, `tabsListVariants`,…
|
||||
- [tabs.variants](./src/components/tabs/tabs.variants.md) — `tabsVariants`, `tabsListVariants`, `tabsTriggerVariants`, and `tabsContentVariants` are the cva (class-variance-author…
|
||||
- [textarea](./src/components/textarea/textarea.md) — <!-- begin:auto-docs --> _(no submodules)_- textarea.variants — `textareaVariants` is the cva (…
|
||||
- [textarea](./src/components/textarea/textarea.md) — <!-- begin:auto-docs --> _(no submodules)_- textarea.variants — `textareaVariants` is the cva (…
|
||||
- [textarea.variants](./src/components/textarea/textarea.variants.md) — `textareaVariants` is the cva (class-variance-authority) configuration that powers every `<Textarea>` render. It is spl…
|
||||
- [theme-toggle](./src/components/theme-toggle/theme-toggle.md) — <!-- begin:auto-docs --> _(no submodules)_- theme-toggle.variants — Cva slot for the chassis `<…
|
||||
- [theme-toggle](./src/components/theme-toggle/theme-toggle.md) — <!-- begin:auto-docs --> _(no submodules)_- theme-toggle.variants — Cva slot for the chassis `<…
|
||||
- [theme-toggle.variants](./src/components/theme-toggle/theme-toggle.variants.md) — Cva slot for the chassis `<ThemeToggle>` icon-button. Mirrors the shape of `<Button variant="outline" size="icon">` so…
|
||||
- [toast](./src/components/toast/toast.md) — <!-- begin:auto-docs --> _(no submodules)_- [toast](./src/components/toast/toast.md) — <!-- begin:auto-docs --> _(no submodules)_- [toggle](./src/components/toggle/toggle.md) — <!-- begin:auto-docs --> _(no submodules)_- toggle.variants — `toggleVariants` is the cva (clas…
|
||||
- [toggle](./src/components/toggle/toggle.md) — <!-- begin:auto-docs --> _(no submodules)_- toggle.variants — `toggleVariants` is the cva (clas…
|
||||
- [toggle.variants](./src/components/toggle/toggle.variants.md) — `toggleVariants` is the cva (class-variance-authority) configuration powering every `<Toggle>` render. It is split from…
|
||||
- [toggle-group](./src/components/toggle-group/toggle-group.md) — <!-- begin:auto-docs --> _(no submodules)_- toggle-group.variants — `toggleGroupVariants` and `…
|
||||
- [toggle-group](./src/components/toggle-group/toggle-group.md) — <!-- begin:auto-docs --> _(no submodules)_- toggle-group.variants — `toggleGroupVariants` and `…
|
||||
- [toggle-group.variants](./src/components/toggle-group/toggle-group.variants.md) — `toggleGroupVariants` and `toggleGroupItemVariants` are the cva (class-variance-authority) configurations powering `<To…
|
||||
- [tooltip](./src/components/tooltip/tooltip.md) — <!-- begin:auto-docs --> _(no submodules)_- tooltip.variants — `tooltipContentVariants` is the……
|
||||
- [tooltip](./src/components/tooltip/tooltip.md) — <!-- begin:auto-docs --> _(no submodules)_- tooltip.variants — `tooltipContentVariants` is the……
|
||||
- [tooltip.variants](./src/components/tooltip/tooltip.variants.md) — `tooltipContentVariants` is the cva configuration powering the `<TooltipContent>` floating bubble. It is split from `to…
|
||||
- [use-toast-sonner-bridge](./src/components/use-toast-sonner-bridge/use-toast-sonner-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-toast-sonner-bridge](./src/components/use-toast-sonner-bridge/use-toast-sonner-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/errors/error-codes.md) — Defines every stable error code emitted by the `@suaveplan/react-ui` package. Codes are grouped under the `REACT_UI` na…
|
||||
- [react-ui-error](./src/errors/react-ui-error.md) — Owns every error thrown by the `@suaveplan/react-ui` package. Extends the project-wide `SuaveplanError` so each instan…
|
||||
- [use-mobile](./src/hooks/use-mobile/use-mobile.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-mobile](./src/hooks/use-mobile/use-mobile.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-toast](./src/hooks/use-toast/use-toast.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-toast](./src/hooks/use-toast/use-toast.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-cell](./src/i18n/formatted-cell/formatted-cell.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-cell](./src/i18n/formatted-cell/formatted-cell.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-currency](./src/i18n/formatted-currency/formatted-currency.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-currency](./src/i18n/formatted-currency/formatted-currency.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-date](./src/i18n/formatted-date/formatted-date.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-date](./src/i18n/formatted-date/formatted-date.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-list](./src/i18n/formatted-list/formatted-list.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-list](./src/i18n/formatted-list/formatted-list.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-message](./src/i18n/formatted-message/formatted-message.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-message](./src/i18n/formatted-message/formatted-message.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-number](./src/i18n/formatted-number/formatted-number.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-number](./src/i18n/formatted-number/formatted-number.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-relative-time](./src/i18n/formatted-relative-time/formatted-relative-time.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-relative-time](./src/i18n/formatted-relative-time/formatted-relative-time.md) — <!-- begin:auto-docs --> _(no submodules)_- [plural](./src/i18n/plural/plural.md) — <!-- begin:auto-docs --> _(no submodules)_- [plural](./src/i18n/plural/plural.md) — <!-- begin:auto-docs --> _(no submodules)_- [react-ui-i18n-bridge](./src/i18n/react-ui-i18n-bridge/react-ui-i18n-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [react-ui-i18n-bridge](./src/i18n/react-ui-i18n-bridge/react-ui-i18n-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [stories-shell](./src/i18n/stories-shell/stories-shell.md) — <!-- begin:auto-docs --> _(no submodules)_- [stories-shell](./src/i18n/stories-shell/stories-shell.md) — <!-- begin:auto-docs --> _(no submodules)_- [tanstack-bridge](./src/i18n/tanstack-bridge/tanstack-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [tanstack-bridge](./src/i18n/tanstack-bridge/tanstack-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [validation-message-bridge](./src/i18n/validation-message-bridge/validation-message-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [validation-message-bridge](./src/i18n/validation-message-bridge/validation-message-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [augmentation](./src/locales/augmentation.md) — Side-effect-only TypeScript module that augments `@suaveplan/react-i18n`'s `ContributedTranslationKeys` interface with…
|
||||
- [schema](./src/locales/schema.md) — Zod schema asserting that every locale bundle declares exactly the keys present in the canonical `en-GB.json` source-of…
|
||||
- [translation-keys](./src/locales/translation-keys.md) — `translation-keys.ts` is the codegen-emitted string-literal union of every translation key contributed by `@suaveplan/r…
|
||||
- [observability](./src/observability/react-ui-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [react-ui-metrics](./src/observability/react-ui-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [pack](./src/pack/pack-context.md) — <!-- begin:auto-docs --> _(no submodules)_- [pack-registry-provider](./src/pack/pack-registry-provider/pack-registry-provider.md) — <!-- begin:auto-docs --> _(no submodules)_- [pack-registry-provider](./src/pack/pack-registry-provider/pack-registry-provider.md) — <!-- begin:auto-docs --> _(no submodules)_- [runtime-override](./src/pack/runtime-override/runtime-override.md) — <!-- begin:auto-docs --> _(no submodules)_- [runtime-override](./src/pack/runtime-override/runtime-override.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-pack-component](./src/pack/use-pack-component/use-pack-component.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-pack-component](./src/pack/use-pack-component/use-pack-component.md) — <!-- begin:auto-docs --> _(no submodules)_- [version-check](./src/pack/version-check/version-check.md) — <!-- begin:auto-docs --> _(no submodules)_- [version-check](./src/pack/version-check/version-check.md) — <!-- begin:auto-docs --> _(no submodules)_- [pack-context](./src/pack/pack-context.md) — <!-- begin:auto-docs --> _(no submodules)_- [react-ui-provider](./src/provider/react-ui-provider/react-ui-provider.md) — <!-- begin:auto-docs --> _(no submodules)_- [react-ui-provider](./src/provider/react-ui-provider/react-ui-provider.md) — <!-- begin:auto-docs --> _(no submodules)_- [theme](./src/theme/theme-context.md) — <!-- begin:auto-docs --> _(no submodules)_- [theme-provider](./src/theme/theme-provider/theme-provider.md) — <!-- begin:auto-docs --> _(no submodules)_- [theme-provider](./src/theme/theme-provider/theme-provider.md) — <!-- begin:auto-docs --> _(no submodules)_- [theme-switcher](./src/theme/theme-switcher/theme-switcher.md) — <!-- begin:auto-docs --> _(no submodules)_- [theme-switcher](./src/theme/theme-switcher/theme-switcher.md) — <!-- begin:auto-docs --> _(no submodules)_- [dark](./src/theme/themes/dark.md) — The dark counterpart of `lightTheme`. Same variant surface, same token names, lightness anchors inverted so foreground…
|
||||
- [high-contrast](./src/theme/themes/high-contrast.md) — Accessibility-first theme that meets WCAG AAA contrast for normal and large text alike. Pure-white foreground (`#FFFFFF…
|
||||
- [light](./src/theme/themes/light.md) — The default `ThemePlugin` instance shipped with `@suaveplan/react-ui`. `lightTheme` matches the shadcn `nova` token pre…
|
||||
- [use-theme](./src/theme/use-theme/use-theme.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-theme](./src/theme/use-theme/use-theme.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-theme-tokens](./src/theme/use-theme-tokens/use-theme-tokens.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-theme-tokens](./src/theme/use-theme-tokens/use-theme-tokens.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-themed-variants](./src/theme/use-themed-variants/use-themed-variants.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-themed-variants](./src/theme/use-themed-variants/use-themed-variants.md) — <!-- begin:auto-docs --> _(no submodules)_- [theme-context](./src/theme/theme-context.md) — <!-- begin:auto-docs --> _(no submodules)_- [cn](./src/utils/cn/cn.md) — <!-- begin:auto-docs --> _(no submodules)_- [cn](./src/utils/cn/cn.md) — <!-- begin:auto-docs --> _(no submodules)_- [motion](./src/utils/motion/motion.md) — <!-- begin:auto-docs --> _(no submodules)_- [motion](./src/utils/motion/motion.md) — <!-- begin:auto-docs --> _(no submodules)_- [variants](./src/variants/variants.md) — <!-- begin:auto-docs --> _(no submodules)_- [variants](./src/variants/variants.md) — <!-- begin:auto-docs --> _(no submodules)_- [version](./src/version.md) — Declares the running version of `@suaveplan/react-ui` as the constant `REACT_UI_VERSION`. The version is hand-maintaine…
|
||||
|
||||
- accordion.variants — `accordionVariants`, `accordio…
|
||||
|
||||
- [accordion](./src/components/accordion/accordion.md) — <!-- begin:auto-docs --> _(no submodules)_- accordion.variants — `accordionVariants`, `accordio…
|
||||
- [accordion.variants](./src/components/accordion/accordion.variants.md) — `accordionVariants`, `accordionItemVariants`, `accordionHeaderVariants`, `accordionTriggerVariants`, and `accordionCont…
|
||||
- [alert](./src/components/alert/alert.md) — <!-- begin:auto-docs --> _(no submodules)_- alert.variants — `alertVariants` is the cva config……
|
||||
- [alert](./src/components/alert/alert.md) — <!-- begin:auto-docs --> _(no submodules)_- alert.variants — `alertVariants` is the cva config……
|
||||
- [alert.variants](./src/components/alert/alert.variants.md) — `alertVariants` is the cva config that powers the `<Alert>` chassis component. It exposes a `variant` axis with four ru…
|
||||
- [alert-dialog](./src/components/alert-dialog/alert-dialog.md) — <!-- begin:auto-docs --> _(no submodules)_- alert-dialog.variants — `alertDialogOverlayVariants…
|
||||
- [alert-dialog](./src/components/alert-dialog/alert-dialog.md) — <!-- begin:auto-docs --> _(no submodules)_- alert-dialog.variants — `alertDialogOverlayVariants…
|
||||
- [alert-dialog.variants](./src/components/alert-dialog/alert-dialog.variants.md) — `alertDialogOverlayVariants`, `alertDialogContentVariants`, `alertDialogHeaderVariants`, `alertDialogFooterVariants`, `…
|
||||
- [aspect-ratio](./src/components/aspect-ratio/aspect-ratio.md) — <!-- begin:auto-docs --> _(no submodules)_- aspect-ratio.variants — `aspectRatioVariants` is th…
|
||||
- [aspect-ratio](./src/components/aspect-ratio/aspect-ratio.md) — <!-- begin:auto-docs --> _(no submodules)_- aspect-ratio.variants — `aspectRatioVariants` is th…
|
||||
- [aspect-ratio.variants](./src/components/aspect-ratio/aspect-ratio.variants.md) — `aspectRatioVariants` is the cva (class-variance-authority) configuration powering the `<AspectRatio>` outer wrapper. I…
|
||||
- [avatar](./src/components/avatar/avatar.md) — <!-- begin:auto-docs --> _(no submodules)_- avatar.variants — `avatarVariants` is the cva confi…
|
||||
- [avatar](./src/components/avatar/avatar.md) — <!-- begin:auto-docs --> _(no submodules)_- avatar.variants — `avatarVariants` is the cva confi…
|
||||
- [avatar.variants](./src/components/avatar/avatar.variants.md) — `avatarVariants` is the cva config that powers the `<Avatar>` chassis component (`<AvatarImage>` and `<AvatarFallback>`…
|
||||
- [badge](./src/components/badge/badge.md) — <!-- begin:auto-docs --> _(no submodules)_- badge.variants — `badgeVariants` is the cva config……
|
||||
- [badge](./src/components/badge/badge.md) — <!-- begin:auto-docs --> _(no submodules)_- badge.variants — `badgeVariants` is the cva config……
|
||||
- [badge.variants](./src/components/badge/badge.variants.md) — `badgeVariants` is the cva config that powers the `<Badge>` chassis component. It exposes a `variant` axis with four ru…
|
||||
- [breadcrumb](./src/components/breadcrumb/breadcrumb.md) — <!-- begin:auto-docs --> _(no submodules)_- breadcrumb.variants — `breadcrumbVariants`, `breadc…
|
||||
- [breadcrumb](./src/components/breadcrumb/breadcrumb.md) — <!-- begin:auto-docs --> _(no submodules)_- breadcrumb.variants — `breadcrumbVariants`, `breadc…
|
||||
- [breadcrumb.variants](./src/components/breadcrumb/breadcrumb.variants.md) — `breadcrumbVariants`, `breadcrumbListVariants`, `breadcrumbItemVariants`, `breadcrumbLinkVariants`, `breadcrumbPageVari…
|
||||
- [button](./src/components/button/button.md) — <!-- begin:auto-docs --> _(no submodules)_- button.variants — `buttonVariants` is the cva (clas…
|
||||
- [button](./src/components/button/button.md) — <!-- begin:auto-docs --> _(no submodules)_- button.variants — `buttonVariants` is the cva (clas…
|
||||
- [button.variants](./src/components/button/button.variants.md) — `buttonVariants` is the cva (class-variance-authority) configuration that powers every `<Button>` render. It is split f…
|
||||
- [calendar](./src/components/calendar/calendar.md) — <!-- begin:auto-docs --> _(no submodules)_- calendar.test-fixtures — Wave-3h local test fixture…
|
||||
- [calendar](./src/components/calendar/calendar.md) — <!-- begin:auto-docs --> _(no submodules)_- calendar.test-fixtures — Wave-3h local test fixture…
|
||||
- [calendar.test-fixtures](./src/components/calendar/calendar.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.calendar.*` keyset. The chas…
|
||||
- [calendar.variants](./src/components/calendar/calendar.variants.md) — `calendarVariants` is the cva config that powers the outer wrapper of `<Calendar>`. The recipe is intentionally minimal…
|
||||
- [card](./src/components/card/card.md) — <!-- begin:auto-docs --> _(no submodules)_- card.variants — `cardVariants` (and its sub-compone…
|
||||
- [card](./src/components/card/card.md) — <!-- begin:auto-docs --> _(no submodules)_- card.variants — `cardVariants` (and its sub-compone…
|
||||
- [card.variants](./src/components/card/card.variants.md) — `cardVariants` (and its sub-component cva resolvers `cardHeaderVariants`, `cardTitleVariants`, `cardDescriptionVariants…
|
||||
- [checkbox](./src/components/checkbox/checkbox.md) — <!-- begin:auto-docs --> _(no submodules)_- checkbox.variants — `checkboxVariants` is the cva (…
|
||||
- [checkbox](./src/components/checkbox/checkbox.md) — <!-- begin:auto-docs --> _(no submodules)_- checkbox.variants — `checkboxVariants` is the cva (…
|
||||
- [checkbox.variants](./src/components/checkbox/checkbox.variants.md) — `checkboxVariants` is the cva (class-variance-authority) configuration that powers every `<Checkbox>` render. It is spl…
|
||||
- [collapsible](./src/components/collapsible/collapsible.md) — <!-- begin:auto-docs --> _(no submodules)_- collapsible.variants — `collapsibleVariants`, `coll…
|
||||
- [collapsible](./src/components/collapsible/collapsible.md) — <!-- begin:auto-docs --> _(no submodules)_- collapsible.variants — `collapsibleVariants`, `coll…
|
||||
- [collapsible.variants](./src/components/collapsible/collapsible.variants.md) — `collapsibleVariants`, `collapsibleTriggerVariants`, and `collapsibleContentVariants` are the cva (class-variance-autho…
|
||||
- [confirm-delete-dialog](./src/components/confirm-delete-dialog/confirm-delete-dialog.md) — <!-- begin:auto-docs --> _(no submodules)_- confirm-delete-dialog.test-fixtures — Wave-3g local…
|
||||
- [confirm-delete-dialog](./src/components/confirm-delete-dialog/confirm-delete-dialog.md) — <!-- begin:auto-docs --> _(no submodules)_- confirm-delete-dialog.test-fixtures — Wave-3g local…
|
||||
- [confirm-delete-dialog.test-fixtures](./src/components/confirm-delete-dialog/confirm-delete-dialog.test-fixtures.md) — Wave-3g local test fixture that augments the chassis test i18n instance with the `react-ui.confirmDelete.*` keyset. The…
|
||||
- [context-menu](./src/components/context-menu/context-menu.md) — <!-- begin:auto-docs --> _(no submodules)_- context-menu.variants — `contextMenuContentVariants…
|
||||
- [context-menu](./src/components/context-menu/context-menu.md) — <!-- begin:auto-docs --> _(no submodules)_- context-menu.variants — `contextMenuContentVariants…
|
||||
- [context-menu.variants](./src/components/context-menu/context-menu.variants.md) — `contextMenuContentVariants`, `contextMenuItemVariants`, `contextMenuLabelVariants`, `contextMenuSeparatorVariants`, `c…
|
||||
- [data-table](./src/components/data-table/data-table.md) — <!-- begin:auto-docs --> _(no submodules)_- data-table.variants — `dataTableVariants`, `dataTab…
|
||||
- [data-table](./src/components/data-table/data-table.md) — <!-- begin:auto-docs --> _(no submodules)_- data-table.variants — `dataTableVariants`, `dataTab…
|
||||
- [data-table.variants](./src/components/data-table/data-table.variants.md) — `dataTableVariants`, `dataTablePaginationVariants`, and `dataTableSortHeaderVariants` drive the chassis `<DataTable>` o…
|
||||
- [dialog](./src/components/dialog/dialog.md) — <!-- begin:auto-docs --> _(no submodules)_- dialog.variants — `dialogOverlayVariants`, `dialogC…
|
||||
- [dialog](./src/components/dialog/dialog.md) — <!-- begin:auto-docs --> _(no submodules)_- dialog.variants — `dialogOverlayVariants`, `dialogC…
|
||||
- [dialog.variants](./src/components/dialog/dialog.variants.md) — `dialogOverlayVariants`, `dialogContentVariants`, `dialogHeaderVariants`, `dialogFooterVariants`, `dialogTitleVariants`…
|
||||
- [drawer](./src/components/drawer/drawer.md) — <!-- begin:auto-docs --> _(no submodules)_- drawer.variants — `drawerOverlayVariants`, `drawerC…
|
||||
- [drawer](./src/components/drawer/drawer.md) — <!-- begin:auto-docs --> _(no submodules)_- drawer.variants — `drawerOverlayVariants`, `drawerC…
|
||||
- [drawer.variants](./src/components/drawer/drawer.variants.md) — `drawerOverlayVariants`, `drawerContentVariants`, `drawerHandleVariants`, `drawerHeaderVariants`, `drawerFooterVariants…
|
||||
- [dropdown-menu](./src/components/dropdown-menu/dropdown-menu.md) — <!-- begin:auto-docs --> _(no submodules)_- dropdown-menu.variants — `dropdownMenuContentVarian…
|
||||
- [dropdown-menu](./src/components/dropdown-menu/dropdown-menu.md) — <!-- begin:auto-docs --> _(no submodules)_- dropdown-menu.variants — `dropdownMenuContentVarian…
|
||||
- [dropdown-menu.variants](./src/components/dropdown-menu/dropdown-menu.variants.md) — `dropdownMenuContentVariants`, `dropdownMenuItemVariants`, `dropdownMenuLabelVariants`, `dropdownMenuSeparatorVariants`…
|
||||
- [empty](./src/components/empty/empty.md) — <!-- begin:auto-docs --> _(no submodules)_- empty.variants — `emptyVariants` is the cva config……
|
||||
- [empty](./src/components/empty/empty.md) — <!-- begin:auto-docs --> _(no submodules)_- empty.variants — `emptyVariants` is the cva config……
|
||||
- [empty.variants](./src/components/empty/empty.variants.md) — `emptyVariants` is the cva config that powers the `<Empty>` chassis component (with `emptyHeaderVariants`, `emptyMediaV…
|
||||
- [error-boundary](./src/components/error-boundary/error-boundary.md) — <!-- begin:auto-docs --> _(no submodules)_- error-boundary.metrics — Co-located OTel counter fo…
|
||||
- [error-boundary](./src/components/error-boundary/error-boundary.md) — <!-- begin:auto-docs --> _(no submodules)_- error-boundary.metrics — Co-located OTel counter fo…
|
||||
- [error-boundary.metrics](./src/components/error-boundary/error-boundary.metrics.md) — Co-located OTel counter for the chassis `<ErrorBoundary>` component. Lives next to the boundary itself rather than in t…
|
||||
- [error-boundary.test-fixtures](./src/components/error-boundary/error-boundary.test-fixtures.md) — Wave-3g local test fixture that augments the chassis test i18n instance with the `react-ui.errorBoundary.*` keyset. The…
|
||||
- [error-boundary.variants](./src/components/error-boundary/error-boundary.variants.md) — Cva slot definitions for the chassis `<ErrorBoundary>` default fallback. Splits the fallback into four named slots — ou…
|
||||
- [field](./src/components/field/field.md) — <!-- begin:auto-docs --> _(no submodules)_- field.variants — `fieldVariants`, `fieldLabelVarian…
|
||||
- [field](./src/components/field/field.md) — <!-- begin:auto-docs --> _(no submodules)_- field.variants — `fieldVariants`, `fieldLabelVarian…
|
||||
- [field.variants](./src/components/field/field.variants.md) — `fieldVariants`, `fieldLabelVariants`, `fieldDescriptionVariants`, and `fieldErrorVariants` are the four cva (class-var…
|
||||
- [field-group](./src/components/field-group/field-group.md) — <!-- begin:auto-docs --> _(no submodules)_- field-group.variants — `fieldGroupVariants` is the……
|
||||
- [field-group](./src/components/field-group/field-group.md) — <!-- begin:auto-docs --> _(no submodules)_- field-group.variants — `fieldGroupVariants` is the……
|
||||
- [field-group.variants](./src/components/field-group/field-group.variants.md) — `fieldGroupVariants` is the cva (class-variance-authority) configuration powering every `<FieldGroup>` render. It is sp…
|
||||
- [field-set](./src/components/field-set/field-set.md) — <!-- begin:auto-docs --> _(no submodules)_- field-set.variants — `fieldSetVariants` and `fieldL…
|
||||
- [field-set](./src/components/field-set/field-set.md) — <!-- begin:auto-docs --> _(no submodules)_- field-set.variants — `fieldSetVariants` and `fieldL…
|
||||
- [field-set.variants](./src/components/field-set/field-set.variants.md) — `fieldSetVariants` and `fieldLegendVariants` are the cva (class-variance-authority) configurations powering `<FieldSet>…
|
||||
- [hover-card](./src/components/hover-card/hover-card.md) — <!-- begin:auto-docs --> _(no submodules)_- hover-card.variants — `hoverCardContentVariants` is…
|
||||
- [hover-card](./src/components/hover-card/hover-card.md) — <!-- begin:auto-docs --> _(no submodules)_- hover-card.variants — `hoverCardContentVariants` is…
|
||||
- [hover-card.variants](./src/components/hover-card/hover-card.variants.md) — `hoverCardContentVariants` is the cva configuration powering the `<HoverCardContent>` floating panel. It is split from…
|
||||
- [input](./src/components/input/input.md) — <!-- begin:auto-docs --> _(no submodules)_- input-number — <!-- begin:au…
|
||||
- [input-number](./src/components/input/input-number/input-number.md) — <!-- begin:auto-docs --> _(no submodules)_- [input-number](./src/components/input/input-number/input-number.md) — <!-- begin:auto-docs --> _(no submodules)_- [input](./src/components/input/input.md) — <!-- begin:auto-docs --> _(no submodules)_- input-number — <!-- begin:au…
|
||||
- [input.variants](./src/components/input/input.variants.md) — `inputVariants` is the cva (class-variance-authority) configuration that powers every `<Input>` render. It is split fro…
|
||||
- [input-group](./src/components/input-group/input-group.md) — <!-- begin:auto-docs --> _(no submodules)_- input-group.variants — `inputGroupVariants`, `input…
|
||||
- [input-group](./src/components/input-group/input-group.md) — <!-- begin:auto-docs --> _(no submodules)_- input-group.variants — `inputGroupVariants`, `input…
|
||||
- [input-group.variants](./src/components/input-group/input-group.variants.md) — `inputGroupVariants`, `inputGroupAddonVariants`, `inputGroupInputVariants`, `inputGroupTextareaVariants`, and `inputGro…
|
||||
- [label](./src/components/label/label.md) — <!-- begin:auto-docs --> _(no submodules)_- label.variants — `labelVariants` is the cva (class-…
|
||||
- [label](./src/components/label/label.md) — <!-- begin:auto-docs --> _(no submodules)_- label.variants — `labelVariants` is the cva (class-…
|
||||
- [label.variants](./src/components/label/label.variants.md) — `labelVariants` is the cva (class-variance-authority) configuration that powers every `<Label>` render. It is split fro…
|
||||
- [menubar](./src/components/menubar/menubar.md) — <!-- begin:auto-docs --> _(no submodules)_- menubar.variants — `menubarVariants`, `menubarTrigg…
|
||||
- [menubar](./src/components/menubar/menubar.md) — <!-- begin:auto-docs --> _(no submodules)_- menubar.variants — `menubarVariants`, `menubarTrigg…
|
||||
- [menubar.variants](./src/components/menubar/menubar.variants.md) — `menubarVariants`, `menubarTriggerVariants`, `menubarContentVariants`, `menubarItemVariants`, `menubarLabelVariants`, `…
|
||||
- [pagination](./src/components/pagination/pagination.md) — <!-- begin:auto-docs --> _(no submodules)_- pagination.variants — `paginationVariants`, `pagina…
|
||||
- [pagination](./src/components/pagination/pagination.md) — <!-- begin:auto-docs --> _(no submodules)_- pagination.variants — `paginationVariants`, `pagina…
|
||||
- [pagination.variants](./src/components/pagination/pagination.variants.md) — `paginationVariants`, `paginationContentVariants`, `paginationItemVariants`, `paginationLinkVariants`, `paginationPrevN…
|
||||
- [popover](./src/components/popover/popover.md) — <!-- begin:auto-docs --> _(no submodules)_- popover.variants — `popoverContentVariants` is the……
|
||||
- [popover](./src/components/popover/popover.md) — <!-- begin:auto-docs --> _(no submodules)_- popover.variants — `popoverContentVariants` is the……
|
||||
- [popover.variants](./src/components/popover/popover.variants.md) — `popoverContentVariants` is the cva configuration powering the `<PopoverContent>` floating panel. It is split from `pop…
|
||||
- [progress](./src/components/progress/progress.md) — <!-- begin:auto-docs --> _(no submodules)_- progress.variants — `progressVariants` is the cva c…
|
||||
- [progress](./src/components/progress/progress.md) — <!-- begin:auto-docs --> _(no submodules)_- progress.variants — `progressVariants` is the cva c…
|
||||
- [progress.variants](./src/components/progress/progress.variants.md) — `progressVariants` is the cva config that powers the `<Progress>` chassis component (paired with `progressIndicatorVari…
|
||||
- [radio-group](./src/components/radio-group/radio-group.md) — <!-- begin:auto-docs --> _(no submodules)_- radio-group.variants — `radioGroupVariants` and `ra…
|
||||
- [radio-group](./src/components/radio-group/radio-group.md) — <!-- begin:auto-docs --> _(no submodules)_- radio-group.variants — `radioGroupVariants` and `ra…
|
||||
- [radio-group.variants](./src/components/radio-group/radio-group.variants.md) — `radioGroupVariants` and `radioGroupItemVariants` are the cva (class-variance-authority) configurations powering `<Radi…
|
||||
- [resizable](./src/components/resizable/resizable.md) — <!-- begin:auto-docs --> _(no submodules)_- resizable.variants — `resizablePanelGroupVariants`,…
|
||||
- [resizable](./src/components/resizable/resizable.md) — <!-- begin:auto-docs --> _(no submodules)_- resizable.variants — `resizablePanelGroupVariants`,…
|
||||
- [resizable.variants](./src/components/resizable/resizable.variants.md) — `resizablePanelGroupVariants`, `resizablePanelVariants`, `resizableHandleVariants`, and `resizableHandleGripVariants` a…
|
||||
- [scroll-area](./src/components/scroll-area/scroll-area.md) — <!-- begin:auto-docs --> _(no submodules)_- scroll-area.variants — `scrollAreaVariants`, `scrol…
|
||||
- [scroll-area](./src/components/scroll-area/scroll-area.md) — <!-- begin:auto-docs --> _(no submodules)_- scroll-area.variants — `scrollAreaVariants`, `scrol…
|
||||
- [scroll-area.variants](./src/components/scroll-area/scroll-area.variants.md) — `scrollAreaVariants`, `scrollAreaViewportVariants`, `scrollAreaScrollbarVariants`, `scrollAreaThumbVariants`, and `scro…
|
||||
- [select](./src/components/select/select.md) — <!-- begin:auto-docs --> _(no submodules)_- select.test-fixtures — Wave-3h local test fixture t…
|
||||
- [select](./src/components/select/select.md) — <!-- begin:auto-docs --> _(no submodules)_- select.test-fixtures — Wave-3h local test fixture t…
|
||||
- [select.test-fixtures](./src/components/select/select.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.select.placeholder` key. The…
|
||||
- [select.variants](./src/components/select/select.variants.md) — The `<Select>` component is composed of six visual slots — trigger, content panel, item row, group label, separator, an…
|
||||
- [separator](./src/components/separator/separator.md) — <!-- begin:auto-docs --> _(no submodules)_- separator.variants — `separatorVariants` is the cva…
|
||||
- [separator](./src/components/separator/separator.md) — <!-- begin:auto-docs --> _(no submodules)_- separator.variants — `separatorVariants` is the cva…
|
||||
- [separator.variants](./src/components/separator/separator.variants.md) — `separatorVariants` is the cva (class-variance-authority) configuration that powers every `<Separator>` render. It is s…
|
||||
- [sheet](./src/components/sheet/sheet.md) — <!-- begin:auto-docs --> _(no submodules)_- sheet.variants — `sheetOverlayVariants`, `sheetCont…
|
||||
- [sheet](./src/components/sheet/sheet.md) — <!-- begin:auto-docs --> _(no submodules)_- sheet.variants — `sheetOverlayVariants`, `sheetCont…
|
||||
- [sheet.variants](./src/components/sheet/sheet.variants.md) — `sheetOverlayVariants`, `sheetContentVariants`, `sheetHeaderVariants`, `sheetFooterVariants`, `sheetTitleVariants`, `sh…
|
||||
- [skeleton](./src/components/skeleton/skeleton.md) — <!-- begin:auto-docs --> _(no submodules)_- skeleton.variants — `skeletonVariants` is the cva (…
|
||||
- [skeleton](./src/components/skeleton/skeleton.md) — <!-- begin:auto-docs --> _(no submodules)_- skeleton.variants — `skeletonVariants` is the cva (…
|
||||
- [skeleton.variants](./src/components/skeleton/skeleton.variants.md) — `skeletonVariants` is the cva (class-variance-authority) configuration that powers every `<Skeleton>` render. It is spl…
|
||||
- [sonner](./src/components/sonner/sonner.md) — <!-- begin:auto-docs --> _(no submodules)_- sonner.variants — Defines the `sonnerToasterVariant…
|
||||
- [sonner](./src/components/sonner/sonner.md) — <!-- begin:auto-docs --> _(no submodules)_- sonner.variants — Defines the `sonnerToasterVariant…
|
||||
- [sonner.variants](./src/components/sonner/sonner.variants.md) — Defines the `sonnerToasterVariants` cva slot for the chassis `<Sonner>` toaster host. Sonner manages its own stacking,…
|
||||
- [spinner](./src/components/spinner/spinner.md) — <!-- begin:auto-docs --> _(no submodules)_- spinner.variants — `spinnerVariants` is the cva con…
|
||||
- [spinner](./src/components/spinner/spinner.md) — <!-- begin:auto-docs --> _(no submodules)_- spinner.variants — `spinnerVariants` is the cva con…
|
||||
- [spinner.variants](./src/components/spinner/spinner.variants.md) — `spinnerVariants` is the cva config that powers the `<Spinner>` chassis component. It exposes a `size` axis (`sm` / `de…
|
||||
- [status-badge](./src/components/status-badge/status-badge.md) — <!-- begin:auto-docs --> _(no submodules)_- status-badge.variants — `statusBadgeVariants` is th…
|
||||
- [status-badge](./src/components/status-badge/status-badge.md) — <!-- begin:auto-docs --> _(no submodules)_- status-badge.variants — `statusBadgeVariants` is th…
|
||||
- [status-badge.variants](./src/components/status-badge/status-badge.variants.md) — `statusBadgeVariants` is the cva config that powers the `<StatusBadge>` chassis component. It exposes a `status` axis w…
|
||||
- [switch](./src/components/switch/switch.md) — <!-- begin:auto-docs --> _(no submodules)_- switch.variants — `switchVariants` and `switchThumb…
|
||||
- [switch](./src/components/switch/switch.md) — <!-- begin:auto-docs --> _(no submodules)_- switch.variants — `switchVariants` and `switchThumb…
|
||||
- [switch.variants](./src/components/switch/switch.variants.md) — `switchVariants` and `switchThumbVariants` are the two cva (class-variance-authority) configurations that together powe…
|
||||
- [table](./src/components/table/table.md) — <!-- begin:auto-docs --> _(no submodules)_- table.variants — `tableVariants` and its eight sub-…
|
||||
- [table](./src/components/table/table.md) — <!-- begin:auto-docs --> _(no submodules)_- table.variants — `tableVariants` and its eight sub-…
|
||||
- [table.variants](./src/components/table/table.variants.md) — `tableVariants` and its eight sub-component cva resolvers (`tableHeaderVariants`, `tableBodyVariants`, `tableFooterVari…
|
||||
- [tabs](./src/components/tabs/tabs.md) — <!-- begin:auto-docs --> _(no submodules)_- tabs.variants — `tabsVariants`, `tabsListVariants`,…
|
||||
- [tabs](./src/components/tabs/tabs.md) — <!-- begin:auto-docs --> _(no submodules)_- tabs.variants — `tabsVariants`, `tabsListVariants`,…
|
||||
- [tabs.variants](./src/components/tabs/tabs.variants.md) — `tabsVariants`, `tabsListVariants`, `tabsTriggerVariants`, and `tabsContentVariants` are the cva (class-variance-author…
|
||||
- [textarea](./src/components/textarea/textarea.md) — <!-- begin:auto-docs --> _(no submodules)_- textarea.variants — `textareaVariants` is the cva (…
|
||||
- [textarea](./src/components/textarea/textarea.md) — <!-- begin:auto-docs --> _(no submodules)_- textarea.variants — `textareaVariants` is the cva (…
|
||||
- [textarea.variants](./src/components/textarea/textarea.variants.md) — `textareaVariants` is the cva (class-variance-authority) configuration that powers every `<Textarea>` render. It is spl…
|
||||
- [theme-toggle](./src/components/theme-toggle/theme-toggle.md) — <!-- begin:auto-docs --> _(no submodules)_- theme-toggle.variants — Cva slot for the chassis `<…
|
||||
- [theme-toggle](./src/components/theme-toggle/theme-toggle.md) — <!-- begin:auto-docs --> _(no submodules)_- theme-toggle.variants — Cva slot for the chassis `<…
|
||||
- [theme-toggle.variants](./src/components/theme-toggle/theme-toggle.variants.md) — Cva slot for the chassis `<ThemeToggle>` icon-button. Mirrors the shape of `<Button variant="outline" size="icon">` so…
|
||||
- [toast](./src/components/toast/toast.md) — <!-- begin:auto-docs --> _(no submodules)_- [toast](./src/components/toast/toast.md) — <!-- begin:auto-docs --> _(no submodules)_- [toggle](./src/components/toggle/toggle.md) — <!-- begin:auto-docs --> _(no submodules)_- toggle.variants — `toggleVariants` is the cva (clas…
|
||||
- [toggle](./src/components/toggle/toggle.md) — <!-- begin:auto-docs --> _(no submodules)_- toggle.variants — `toggleVariants` is the cva (clas…
|
||||
- [toggle.variants](./src/components/toggle/toggle.variants.md) — `toggleVariants` is the cva (class-variance-authority) configuration powering every `<Toggle>` render. It is split from…
|
||||
- [toggle-group](./src/components/toggle-group/toggle-group.md) — <!-- begin:auto-docs --> _(no submodules)_- toggle-group.variants — `toggleGroupVariants` and `…
|
||||
- [toggle-group](./src/components/toggle-group/toggle-group.md) — <!-- begin:auto-docs --> _(no submodules)_- toggle-group.variants — `toggleGroupVariants` and `…
|
||||
- [toggle-group.variants](./src/components/toggle-group/toggle-group.variants.md) — `toggleGroupVariants` and `toggleGroupItemVariants` are the cva (class-variance-authority) configurations powering `<To…
|
||||
- [tooltip](./src/components/tooltip/tooltip.md) — <!-- begin:auto-docs --> _(no submodules)_- tooltip.variants — `tooltipContentVariants` is the……
|
||||
- [tooltip](./src/components/tooltip/tooltip.md) — <!-- begin:auto-docs --> _(no submodules)_- tooltip.variants — `tooltipContentVariants` is the……
|
||||
- [tooltip.variants](./src/components/tooltip/tooltip.variants.md) — `tooltipContentVariants` is the cva configuration powering the `<TooltipContent>` floating bubble. It is split from `to…
|
||||
- [use-toast-sonner-bridge](./src/components/use-toast-sonner-bridge/use-toast-sonner-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-toast-sonner-bridge](./src/components/use-toast-sonner-bridge/use-toast-sonner-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/errors/error-codes.md) — Defines every stable error code emitted by the `@suaveplan/react-ui` package. Codes are grouped under the `REACT_UI` na…
|
||||
- [react-ui-error](./src/errors/react-ui-error.md) — Owns every error thrown by the `@suaveplan/react-ui` package. Extends the project-wide `SuaveplanError` so each instan…
|
||||
- [use-mobile](./src/hooks/use-mobile/use-mobile.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-mobile](./src/hooks/use-mobile/use-mobile.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-toast](./src/hooks/use-toast/use-toast.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-toast](./src/hooks/use-toast/use-toast.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-cell](./src/i18n/formatted-cell/formatted-cell.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-cell](./src/i18n/formatted-cell/formatted-cell.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-currency](./src/i18n/formatted-currency/formatted-currency.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-currency](./src/i18n/formatted-currency/formatted-currency.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-date](./src/i18n/formatted-date/formatted-date.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-date](./src/i18n/formatted-date/formatted-date.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-list](./src/i18n/formatted-list/formatted-list.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-list](./src/i18n/formatted-list/formatted-list.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-message](./src/i18n/formatted-message/formatted-message.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-message](./src/i18n/formatted-message/formatted-message.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-number](./src/i18n/formatted-number/formatted-number.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-number](./src/i18n/formatted-number/formatted-number.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-relative-time](./src/i18n/formatted-relative-time/formatted-relative-time.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-relative-time](./src/i18n/formatted-relative-time/formatted-relative-time.md) — <!-- begin:auto-docs --> _(no submodules)_- [plural](./src/i18n/plural/plural.md) — <!-- begin:auto-docs --> _(no submodules)_- [plural](./src/i18n/plural/plural.md) — <!-- begin:auto-docs --> _(no submodules)_- [react-ui-i18n-bridge](./src/i18n/react-ui-i18n-bridge/react-ui-i18n-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [react-ui-i18n-bridge](./src/i18n/react-ui-i18n-bridge/react-ui-i18n-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [stories-shell](./src/i18n/stories-shell/stories-shell.md) — <!-- begin:auto-docs --> _(no submodules)_- [stories-shell](./src/i18n/stories-shell/stories-shell.md) — <!-- begin:auto-docs --> _(no submodules)_- [tanstack-bridge](./src/i18n/tanstack-bridge/tanstack-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [tanstack-bridge](./src/i18n/tanstack-bridge/tanstack-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [validation-message-bridge](./src/i18n/validation-message-bridge/validation-message-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [validation-message-bridge](./src/i18n/validation-message-bridge/validation-message-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [augmentation](./src/locales/augmentation.md) — Side-effect-only TypeScript module that augments `@suaveplan/react-i18n`'s `ContributedTranslationKeys` interface with…
|
||||
- [schema](./src/locales/schema.md) — Zod schema asserting that every locale bundle declares exactly the keys present in the canonical `en-GB.json` source-of…
|
||||
- [translation-keys](./src/locales/translation-keys.md) — `translation-keys.ts` is the codegen-emitted string-literal union of every translation key contributed by `@suaveplan/r…
|
||||
- [observability](./src/observability/react-ui-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [react-ui-metrics](./src/observability/react-ui-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [pack](./src/pack/pack-context.md) — <!-- begin:auto-docs --> _(no submodules)_- pack-registry-provider — <!-…
|
||||
- [pack-registry-provider](./src/pack/pack-registry-provider/pack-registry-provider.md) — <!-- begin:auto-docs --> _(no submodules)_- [pack-registry-provider](./src/pack/pack-registry-provider/pack-registry-provider.md) — <!-- begin:auto-docs --> _(no submodules)_- [runtime-override](./src/pack/runtime-override/runtime-override.md) — <!-- begin:auto-docs --> _(no submodules)_- [runtime-override](./src/pack/runtime-override/runtime-override.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-pack-component](./src/pack/use-pack-component/use-pack-component.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-pack-component](./src/pack/use-pack-component/use-pack-component.md) — <!-- begin:auto-docs --> _(no submodules)_- [version-check](./src/pack/version-check/version-check.md) — <!-- begin:auto-docs --> _(no submodules)_- [version-check](./src/pack/version-check/version-check.md) — <!-- begin:auto-docs --> _(no submodules)_- [pack-context](./src/pack/pack-context.md) — <!-- begin:auto-docs --> _(no submodules)_- pack-registry-provider — <!-…
|
||||
- [react-ui-provider](./src/provider/react-ui-provider/react-ui-provider.md) — <!-- begin:auto-docs --> _(no submodules)_- [react-ui-provider](./src/provider/react-ui-provider/react-ui-provider.md) — <!-- begin:auto-docs --> _(no submodules)_- [theme](./src/theme/theme-context.md) — <!-- begin:auto-docs --> _(no submodules)_- theme-provider — <!-- begin:…
|
||||
- [theme-provider](./src/theme/theme-provider/theme-provider.md) — <!-- begin:auto-docs --> _(no submodules)_- [theme-provider](./src/theme/theme-provider/theme-provider.md) — <!-- begin:auto-docs --> _(no submodules)_- [theme-switcher](./src/theme/theme-switcher/theme-switcher.md) — <!-- begin:auto-docs --> _(no submodules)_- [theme-switcher](./src/theme/theme-switcher/theme-switcher.md) — <!-- begin:auto-docs --> _(no submodules)_- [dark](./src/theme/themes/dark.md) — The dark counterpart of `lightTheme`. Same variant surface, same token names, lightness anchors inverted so foreground…
|
||||
- [high-contrast](./src/theme/themes/high-contrast.md) — Accessibility-first theme that meets WCAG AAA contrast for normal and large text alike. Pure-white foreground (`#FFFFFF…
|
||||
- [light](./src/theme/themes/light.md) — The default `ThemePlugin` instance shipped with `@suaveplan/react-ui`. `lightTheme` matches the shadcn `nova` token pre…
|
||||
- [use-theme](./src/theme/use-theme/use-theme.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-theme](./src/theme/use-theme/use-theme.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-theme-tokens](./src/theme/use-theme-tokens/use-theme-tokens.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-theme-tokens](./src/theme/use-theme-tokens/use-theme-tokens.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-themed-variants](./src/theme/use-themed-variants/use-themed-variants.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-themed-variants](./src/theme/use-themed-variants/use-themed-variants.md) — <!-- begin:auto-docs --> _(no submodules)_- [theme-context](./src/theme/theme-context.md) — <!-- begin:auto-docs --> _(no submodules)_- theme-provider — <!-- begin:…
|
||||
- [cn](./src/utils/cn/cn.md) — <!-- begin:auto-docs --> _(no submodules)_- [cn](./src/utils/cn/cn.md) — <!-- begin:auto-docs --> _(no submodules)_- [motion](./src/utils/motion/motion.md) — <!-- begin:auto-docs --> _(no submodules)_- [motion](./src/utils/motion/motion.md) — <!-- begin:auto-docs --> _(no submodules)_- [variants](./src/variants/variants.md) — <!-- begin:auto-docs --> _(no submodules)_- [variants](./src/variants/variants.md) — <!-- begin:auto-docs --> _(no submodules)_- [version](./src/version.md) — Declares the running version of `@suaveplan/react-ui` as the constant `REACT_UI_VERSION`. The version is hand-maintaine…
|
||||
|
||||
- accordion.variants — `accordionVariants`, `accordio…
|
||||
|
||||
- [accordion](./src/components/accordion/accordion.md) — <!-- begin:auto-docs --> _(no submodules)_- accordion.variants — `accordionVariants`, `accordio…
|
||||
- [accordion.variants](./src/components/accordion/accordion.variants.md) — `accordionVariants`, `accordionItemVariants`, `accordionHeaderVariants`, `accordionTriggerVariants`, and `accordionCont…
|
||||
- [alert](./src/components/alert/alert.md) — <!-- begin:auto-docs --> _(no submodules)_- alert.variants — `alertVariants` is the cva config…
|
||||
- [alert](./src/components/alert/alert.md) — <!-- begin:auto-docs --> _(no submodules)_- alert.variants — `alertVariants` is the cva config…
|
||||
- [alert.variants](./src/components/alert/alert.variants.md) — `alertVariants` is the cva config that powers the `<Alert>` chassis component. It exposes a `variant` axis with four ru…
|
||||
- [alert-dialog](./src/components/alert-dialog/alert-dialog.md) — <!-- begin:auto-docs --> _(no submodules)_- alert-dialog.variants — `alertDialogOverlayVariants…
|
||||
- [alert-dialog](./src/components/alert-dialog/alert-dialog.md) — <!-- begin:auto-docs --> _(no submodules)_- alert-dialog.variants — `alertDialogOverlayVariants…
|
||||
- [alert-dialog.variants](./src/components/alert-dialog/alert-dialog.variants.md) — `alertDialogOverlayVariants`, `alertDialogContentVariants`, `alertDialogHeaderVariants`, `alertDialogFooterVariants`, `…
|
||||
- [aspect-ratio](./src/components/aspect-ratio/aspect-ratio.md) — <!-- begin:auto-docs --> _(no submodules)_- aspect-ratio.variants — `aspectRatioVariants` is th…
|
||||
- [aspect-ratio](./src/components/aspect-ratio/aspect-ratio.md) — <!-- begin:auto-docs --> _(no submodules)_- aspect-ratio.variants — `aspectRatioVariants` is th…
|
||||
- [aspect-ratio.variants](./src/components/aspect-ratio/aspect-ratio.variants.md) — `aspectRatioVariants` is the cva (class-variance-authority) configuration powering the `<AspectRatio>` outer wrapper. I…
|
||||
- [avatar](./src/components/avatar/avatar.md) — <!-- begin:auto-docs --> _(no submodules)_- avatar.variants — `avatarVariants` is the cva confi…
|
||||
- [avatar](./src/components/avatar/avatar.md) — <!-- begin:auto-docs --> _(no submodules)_- avatar.variants — `avatarVariants` is the cva confi…
|
||||
- [avatar.variants](./src/components/avatar/avatar.variants.md) — `avatarVariants` is the cva config that powers the `<Avatar>` chassis component (`<AvatarImage>` and `<AvatarFallback>`…
|
||||
- [badge](./src/components/badge/badge.md) — <!-- begin:auto-docs --> _(no submodules)_- badge.variants — `badgeVariants` is the cva config…
|
||||
- [badge](./src/components/badge/badge.md) — <!-- begin:auto-docs --> _(no submodules)_- badge.variants — `badgeVariants` is the cva config…
|
||||
- [badge.variants](./src/components/badge/badge.variants.md) — `badgeVariants` is the cva config that powers the `<Badge>` chassis component. It exposes a `variant` axis with four ru…
|
||||
- [breadcrumb](./src/components/breadcrumb/breadcrumb.md) — <!-- begin:auto-docs --> _(no submodules)_- breadcrumb.variants — `breadcrumbVariants`, `breadc…
|
||||
- [breadcrumb](./src/components/breadcrumb/breadcrumb.md) — <!-- begin:auto-docs --> _(no submodules)_- breadcrumb.variants — `breadcrumbVariants`, `breadc…
|
||||
- [breadcrumb.variants](./src/components/breadcrumb/breadcrumb.variants.md) — `breadcrumbVariants`, `breadcrumbListVariants`, `breadcrumbItemVariants`, `breadcrumbLinkVariants`, `breadcrumbPageVari…
|
||||
- [button](./src/components/button/button.md) — <!-- begin:auto-docs --> _(no submodules)_- button.variants — `buttonVariants` is the cva (clas…
|
||||
- [button](./src/components/button/button.md) — <!-- begin:auto-docs --> _(no submodules)_- button.variants — `buttonVariants` is the cva (clas…
|
||||
- [button.variants](./src/components/button/button.variants.md) — `buttonVariants` is the cva (class-variance-authority) configuration that powers every `<Button>` render. It is split f…
|
||||
- [calendar](./src/components/calendar/calendar.md) — <!-- begin:auto-docs --> _(no submodules)_- calendar.test-fixtures — Wave-3h local test fixture…
|
||||
- [calendar](./src/components/calendar/calendar.md) — <!-- begin:auto-docs --> _(no submodules)_- calendar.test-fixtures — Wave-3h local test fixture…
|
||||
- [calendar.test-fixtures](./src/components/calendar/calendar.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.calendar.*` keyset. The chas…
|
||||
- [calendar.variants](./src/components/calendar/calendar.variants.md) — `calendarVariants` is the cva config that powers the outer wrapper of `<Calendar>`. The recipe is intentionally minimal…
|
||||
- [card](./src/components/card/card.md) — <!-- begin:auto-docs --> _(no submodules)_- card.variants — `cardVariants` (and its sub-compone…
|
||||
- [card](./src/components/card/card.md) — <!-- begin:auto-docs --> _(no submodules)_- card.variants — `cardVariants` (and its sub-compone…
|
||||
- [card.variants](./src/components/card/card.variants.md) — `cardVariants` (and its sub-component cva resolvers `cardHeaderVariants`, `cardTitleVariants`, `cardDescriptionVariants…
|
||||
- [checkbox](./src/components/checkbox/checkbox.md) — <!-- begin:auto-docs --> _(no submodules)_- checkbox.variants — `checkboxVariants` is the cva (…
|
||||
- [checkbox](./src/components/checkbox/checkbox.md) — <!-- begin:auto-docs --> _(no submodules)_- checkbox.variants — `checkboxVariants` is the cva (…
|
||||
- [checkbox.variants](./src/components/checkbox/checkbox.variants.md) — `checkboxVariants` is the cva (class-variance-authority) configuration that powers every `<Checkbox>` render. It is spl…
|
||||
- [collapsible](./src/components/collapsible/collapsible.md) — <!-- begin:auto-docs --> _(no submodules)_- collapsible.variants — `collapsibleVariants`, `coll…
|
||||
- [collapsible](./src/components/collapsible/collapsible.md) — <!-- begin:auto-docs --> _(no submodules)_- collapsible.variants — `collapsibleVariants`, `coll…
|
||||
- [collapsible.variants](./src/components/collapsible/collapsible.variants.md) — `collapsibleVariants`, `collapsibleTriggerVariants`, and `collapsibleContentVariants` are the cva (class-variance-autho…
|
||||
- [confirm-delete-dialog](./src/components/confirm-delete-dialog/confirm-delete-dialog.md) — <!-- begin:auto-docs --> _(no submodules)_- confirm-delete-dialog.test-fixtures — Wave-3g local…
|
||||
- [confirm-delete-dialog](./src/components/confirm-delete-dialog/confirm-delete-dialog.md) — <!-- begin:auto-docs --> _(no submodules)_- confirm-delete-dialog.test-fixtures — Wave-3g local…
|
||||
- [confirm-delete-dialog.test-fixtures](./src/components/confirm-delete-dialog/confirm-delete-dialog.test-fixtures.md) — Wave-3g local test fixture that augments the chassis test i18n instance with the `react-ui.confirmDelete.*` keyset. The…
|
||||
- [context-menu](./src/components/context-menu/context-menu.md) — <!-- begin:auto-docs --> _(no submodules)_- context-menu.variants — `contextMenuContentVariants…
|
||||
- [context-menu](./src/components/context-menu/context-menu.md) — <!-- begin:auto-docs --> _(no submodules)_- context-menu.variants — `contextMenuContentVariants…
|
||||
- [context-menu.variants](./src/components/context-menu/context-menu.variants.md) — `contextMenuContentVariants`, `contextMenuItemVariants`, `contextMenuLabelVariants`, `contextMenuSeparatorVariants`, `c…
|
||||
- [data-table](./src/components/data-table/data-table.md) — <!-- begin:auto-docs --> _(no submodules)_- data-table.variants — `dataTableVariants`, `dataTab…
|
||||
- [data-table](./src/components/data-table/data-table.md) — <!-- begin:auto-docs --> _(no submodules)_- data-table.variants — `dataTableVariants`, `dataTab…
|
||||
- [data-table.variants](./src/components/data-table/data-table.variants.md) — `dataTableVariants`, `dataTablePaginationVariants`, and `dataTableSortHeaderVariants` drive the chassis `<DataTable>` o…
|
||||
- [dialog](./src/components/dialog/dialog.md) — <!-- begin:auto-docs --> _(no submodules)_- dialog.variants — `dialogOverlayVariants`, `dialogC…
|
||||
- [dialog](./src/components/dialog/dialog.md) — <!-- begin:auto-docs --> _(no submodules)_- dialog.variants — `dialogOverlayVariants`, `dialogC…
|
||||
- [dialog.variants](./src/components/dialog/dialog.variants.md) — `dialogOverlayVariants`, `dialogContentVariants`, `dialogHeaderVariants`, `dialogFooterVariants`, `dialogTitleVariants`…
|
||||
- [drawer](./src/components/drawer/drawer.md) — <!-- begin:auto-docs --> _(no submodules)_- drawer.variants — `drawerOverlayVariants`, `drawerC…
|
||||
- [drawer](./src/components/drawer/drawer.md) — <!-- begin:auto-docs --> _(no submodules)_- drawer.variants — `drawerOverlayVariants`, `drawerC…
|
||||
- [drawer.variants](./src/components/drawer/drawer.variants.md) — `drawerOverlayVariants`, `drawerContentVariants`, `drawerHandleVariants`, `drawerHeaderVariants`, `drawerFooterVariants…
|
||||
- [dropdown-menu](./src/components/dropdown-menu/dropdown-menu.md) — <!-- begin:auto-docs --> _(no submodules)_- dropdown-menu.variants — `dropdownMenuContentVarian…
|
||||
- [dropdown-menu](./src/components/dropdown-menu/dropdown-menu.md) — <!-- begin:auto-docs --> _(no submodules)_- dropdown-menu.variants — `dropdownMenuContentVarian…
|
||||
- [dropdown-menu.variants](./src/components/dropdown-menu/dropdown-menu.variants.md) — `dropdownMenuContentVariants`, `dropdownMenuItemVariants`, `dropdownMenuLabelVariants`, `dropdownMenuSeparatorVariants`…
|
||||
- [empty](./src/components/empty/empty.md) — <!-- begin:auto-docs --> _(no submodules)_- empty.variants — `emptyVariants` is the cva config…
|
||||
- [empty](./src/components/empty/empty.md) — <!-- begin:auto-docs --> _(no submodules)_- empty.variants — `emptyVariants` is the cva config…
|
||||
- [empty.variants](./src/components/empty/empty.variants.md) — `emptyVariants` is the cva config that powers the `<Empty>` chassis component (with `emptyHeaderVariants`, `emptyMediaV…
|
||||
- [error-boundary](./src/components/error-boundary/error-boundary.md) — <!-- begin:auto-docs --> _(no submodules)_- error-boundary.metrics — Co-located OTel counter fo…
|
||||
- [error-boundary](./src/components/error-boundary/error-boundary.md) — <!-- begin:auto-docs --> _(no submodules)_- error-boundary.metrics — Co-located OTel counter fo…
|
||||
- [error-boundary.metrics](./src/components/error-boundary/error-boundary.metrics.md) — Co-located OTel counter for the chassis `<ErrorBoundary>` component. Lives next to the boundary itself rather than in t…
|
||||
- [error-boundary.test-fixtures](./src/components/error-boundary/error-boundary.test-fixtures.md) — Wave-3g local test fixture that augments the chassis test i18n instance with the `react-ui.errorBoundary.*` keyset. The…
|
||||
- [error-boundary.variants](./src/components/error-boundary/error-boundary.variants.md) — Cva slot definitions for the chassis `<ErrorBoundary>` default fallback. Splits the fallback into four named slots — ou…
|
||||
- [field](./src/components/field/field.md) — <!-- begin:auto-docs --> _(no submodules)_- field.variants — `fieldVariants`, `fieldLabelVarian…
|
||||
- [field](./src/components/field/field.md) — <!-- begin:auto-docs --> _(no submodules)_- field.variants — `fieldVariants`, `fieldLabelVarian…
|
||||
- [field.variants](./src/components/field/field.variants.md) — `fieldVariants`, `fieldLabelVariants`, `fieldDescriptionVariants`, and `fieldErrorVariants` are the four cva (class-var…
|
||||
- [field-group](./src/components/field-group/field-group.md) — <!-- begin:auto-docs --> _(no submodules)_- field-group.variants — `fieldGroupVariants` is the…
|
||||
- [field-group](./src/components/field-group/field-group.md) — <!-- begin:auto-docs --> _(no submodules)_- field-group.variants — `fieldGroupVariants` is the…
|
||||
- [field-group.variants](./src/components/field-group/field-group.variants.md) — `fieldGroupVariants` is the cva (class-variance-authority) configuration powering every `<FieldGroup>` render. It is sp…
|
||||
- [field-set](./src/components/field-set/field-set.md) — <!-- begin:auto-docs --> _(no submodules)_- field-set.variants — `fieldSetVariants` and `fieldL…
|
||||
- [field-set](./src/components/field-set/field-set.md) — <!-- begin:auto-docs --> _(no submodules)_- field-set.variants — `fieldSetVariants` and `fieldL…
|
||||
- [field-set.variants](./src/components/field-set/field-set.variants.md) — `fieldSetVariants` and `fieldLegendVariants` are the cva (class-variance-authority) configurations powering `<FieldSet>…
|
||||
- [hover-card](./src/components/hover-card/hover-card.md) — <!-- begin:auto-docs --> _(no submodules)_- hover-card.variants — `hoverCardContentVariants` is…
|
||||
- [hover-card](./src/components/hover-card/hover-card.md) — <!-- begin:auto-docs --> _(no submodules)_- hover-card.variants — `hoverCardContentVariants` is…
|
||||
- [hover-card.variants](./src/components/hover-card/hover-card.variants.md) — `hoverCardContentVariants` is the cva configuration powering the `<HoverCardContent>` floating panel. It is split from…
|
||||
- [input](./src/components/input/input.md) — <!-- begin:auto-docs --> _(no submodules)_- input-number — <!-- begin:auto-docs --> _(no submod…
|
||||
- [input-number](./src/components/input/input-number/input-number.md) — <!-- begin:auto-docs --> _(no submodules)_- [input-number](./src/components/input/input-number/input-number.md) — <!-- begin:auto-docs --> _(no submodules)_- [input](./src/components/input/input.md) — <!-- begin:auto-docs --> _(no submodules)_- input-number — <!-- begin:auto-docs --> _(no submod…
|
||||
- [input.variants](./src/components/input/input.variants.md) — `inputVariants` is the cva (class-variance-authority) configuration that powers every `<Input>` render. It is split fro…
|
||||
- [input-group](./src/components/input-group/input-group.md) — <!-- begin:auto-docs --> _(no submodules)_- input-group.variants — `inputGroupVariants`, `input…
|
||||
- [input-group](./src/components/input-group/input-group.md) — <!-- begin:auto-docs --> _(no submodules)_- input-group.variants — `inputGroupVariants`, `input…
|
||||
- [input-group.variants](./src/components/input-group/input-group.variants.md) — `inputGroupVariants`, `inputGroupAddonVariants`, `inputGroupInputVariants`, `inputGroupTextareaVariants`, and `inputGro…
|
||||
- [label](./src/components/label/label.md) — <!-- begin:auto-docs --> _(no submodules)_- label.variants — `labelVariants` is the cva (class-…
|
||||
- [label](./src/components/label/label.md) — <!-- begin:auto-docs --> _(no submodules)_- label.variants — `labelVariants` is the cva (class-…
|
||||
- [label.variants](./src/components/label/label.variants.md) — `labelVariants` is the cva (class-variance-authority) configuration that powers every `<Label>` render. It is split fro…
|
||||
- [menubar](./src/components/menubar/menubar.md) — <!-- begin:auto-docs --> _(no submodules)_- menubar.variants — `menubarVariants`, `menubarTrigg…
|
||||
- [menubar](./src/components/menubar/menubar.md) — <!-- begin:auto-docs --> _(no submodules)_- menubar.variants — `menubarVariants`, `menubarTrigg…
|
||||
- [menubar.variants](./src/components/menubar/menubar.variants.md) — `menubarVariants`, `menubarTriggerVariants`, `menubarContentVariants`, `menubarItemVariants`, `menubarLabelVariants`, `…
|
||||
- [pagination](./src/components/pagination/pagination.md) — <!-- begin:auto-docs --> _(no submodules)_- pagination.variants — `paginationVariants`, `pagina…
|
||||
- [pagination](./src/components/pagination/pagination.md) — <!-- begin:auto-docs --> _(no submodules)_- pagination.variants — `paginationVariants`, `pagina…
|
||||
- [pagination.variants](./src/components/pagination/pagination.variants.md) — `paginationVariants`, `paginationContentVariants`, `paginationItemVariants`, `paginationLinkVariants`, `paginationPrevN…
|
||||
- [popover](./src/components/popover/popover.md) — <!-- begin:auto-docs --> _(no submodules)_- popover.variants — `popoverContentVariants` is the…
|
||||
- [popover](./src/components/popover/popover.md) — <!-- begin:auto-docs --> _(no submodules)_- popover.variants — `popoverContentVariants` is the…
|
||||
- [popover.variants](./src/components/popover/popover.variants.md) — `popoverContentVariants` is the cva configuration powering the `<PopoverContent>` floating panel. It is split from `pop…
|
||||
- [progress](./src/components/progress/progress.md) — <!-- begin:auto-docs --> _(no submodules)_- progress.variants — `progressVariants` is the cva c…
|
||||
- [progress](./src/components/progress/progress.md) — <!-- begin:auto-docs --> _(no submodules)_- progress.variants — `progressVariants` is the cva c…
|
||||
- [progress.variants](./src/components/progress/progress.variants.md) — `progressVariants` is the cva config that powers the `<Progress>` chassis component (paired with `progressIndicatorVari…
|
||||
- [radio-group](./src/components/radio-group/radio-group.md) — <!-- begin:auto-docs --> _(no submodules)_- radio-group.variants — `radioGroupVariants` and `ra…
|
||||
- [radio-group](./src/components/radio-group/radio-group.md) — <!-- begin:auto-docs --> _(no submodules)_- radio-group.variants — `radioGroupVariants` and `ra…
|
||||
- [radio-group.variants](./src/components/radio-group/radio-group.variants.md) — `radioGroupVariants` and `radioGroupItemVariants` are the cva (class-variance-authority) configurations powering `<Radi…
|
||||
- [resizable](./src/components/resizable/resizable.md) — <!-- begin:auto-docs --> _(no submodules)_- resizable.variants — `resizablePanelGroupVariants`,…
|
||||
- [resizable](./src/components/resizable/resizable.md) — <!-- begin:auto-docs --> _(no submodules)_- resizable.variants — `resizablePanelGroupVariants`,…
|
||||
- [resizable.variants](./src/components/resizable/resizable.variants.md) — `resizablePanelGroupVariants`, `resizablePanelVariants`, `resizableHandleVariants`, and `resizableHandleGripVariants` a…
|
||||
- [scroll-area](./src/components/scroll-area/scroll-area.md) — <!-- begin:auto-docs --> _(no submodules)_- scroll-area.variants — `scrollAreaVariants`, `scrol…
|
||||
- [scroll-area](./src/components/scroll-area/scroll-area.md) — <!-- begin:auto-docs --> _(no submodules)_- scroll-area.variants — `scrollAreaVariants`, `scrol…
|
||||
- [scroll-area.variants](./src/components/scroll-area/scroll-area.variants.md) — `scrollAreaVariants`, `scrollAreaViewportVariants`, `scrollAreaScrollbarVariants`, `scrollAreaThumbVariants`, and `scro…
|
||||
- [select](./src/components/select/select.md) — <!-- begin:auto-docs --> _(no submodules)_- select.test-fixtures — Wave-3h local test fixture t…
|
||||
- [select](./src/components/select/select.md) — <!-- begin:auto-docs --> _(no submodules)_- select.test-fixtures — Wave-3h local test fixture t…
|
||||
- [select.test-fixtures](./src/components/select/select.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.select.placeholder` key. The…
|
||||
- [select.variants](./src/components/select/select.variants.md) — The `<Select>` component is composed of six visual slots — trigger, content panel, item row, group label, separator, an…
|
||||
- [separator](./src/components/separator/separator.md) — <!-- begin:auto-docs --> _(no submodules)_- separator.variants — `separatorVariants` is the cva…
|
||||
- [separator](./src/components/separator/separator.md) — <!-- begin:auto-docs --> _(no submodules)_- separator.variants — `separatorVariants` is the cva…
|
||||
- [separator.variants](./src/components/separator/separator.variants.md) — `separatorVariants` is the cva (class-variance-authority) configuration that powers every `<Separator>` render. It is s…
|
||||
- [sheet](./src/components/sheet/sheet.md) — <!-- begin:auto-docs --> _(no submodules)_- sheet.variants — `sheetOverlayVariants`, `sheetCont…
|
||||
- [sheet](./src/components/sheet/sheet.md) — <!-- begin:auto-docs --> _(no submodules)_- sheet.variants — `sheetOverlayVariants`, `sheetCont…
|
||||
- [sheet.variants](./src/components/sheet/sheet.variants.md) — `sheetOverlayVariants`, `sheetContentVariants`, `sheetHeaderVariants`, `sheetFooterVariants`, `sheetTitleVariants`, `sh…
|
||||
- [skeleton](./src/components/skeleton/skeleton.md) — <!-- begin:auto-docs --> _(no submodules)_- skeleton.variants — `skeletonVariants` is the cva (…
|
||||
- [skeleton](./src/components/skeleton/skeleton.md) — <!-- begin:auto-docs --> _(no submodules)_- skeleton.variants — `skeletonVariants` is the cva (…
|
||||
- [skeleton.variants](./src/components/skeleton/skeleton.variants.md) — `skeletonVariants` is the cva (class-variance-authority) configuration that powers every `<Skeleton>` render. It is spl…
|
||||
- [sonner](./src/components/sonner/sonner.md) — <!-- begin:auto-docs --> _(no submodules)_- sonner.variants — Defines the `sonnerToasterVariant…
|
||||
- [sonner](./src/components/sonner/sonner.md) — <!-- begin:auto-docs --> _(no submodules)_- sonner.variants — Defines the `sonnerToasterVariant…
|
||||
- [sonner.variants](./src/components/sonner/sonner.variants.md) — Defines the `sonnerToasterVariants` cva slot for the chassis `<Sonner>` toaster host. Sonner manages its own stacking,…
|
||||
- [spinner](./src/components/spinner/spinner.md) — <!-- begin:auto-docs --> _(no submodules)_- spinner.variants — `spinnerVariants` is the cva con…
|
||||
- [spinner](./src/components/spinner/spinner.md) — <!-- begin:auto-docs --> _(no submodules)_- spinner.variants — `spinnerVariants` is the cva con…
|
||||
- [spinner.variants](./src/components/spinner/spinner.variants.md) — `spinnerVariants` is the cva config that powers the `<Spinner>` chassis component. It exposes a `size` axis (`sm` / `de…
|
||||
- [status-badge](./src/components/status-badge/status-badge.md) — <!-- begin:auto-docs --> _(no submodules)_- status-badge.variants — `statusBadgeVariants` is th…
|
||||
- [status-badge](./src/components/status-badge/status-badge.md) — <!-- begin:auto-docs --> _(no submodules)_- status-badge.variants — `statusBadgeVariants` is th…
|
||||
- [status-badge.variants](./src/components/status-badge/status-badge.variants.md) — `statusBadgeVariants` is the cva config that powers the `<StatusBadge>` chassis component. It exposes a `status` axis w…
|
||||
- [switch](./src/components/switch/switch.md) — <!-- begin:auto-docs --> _(no submodules)_- switch.variants — `switchVariants` and `switchThumb…
|
||||
- [switch](./src/components/switch/switch.md) — <!-- begin:auto-docs --> _(no submodules)_- switch.variants — `switchVariants` and `switchThumb…
|
||||
- [switch.variants](./src/components/switch/switch.variants.md) — `switchVariants` and `switchThumbVariants` are the two cva (class-variance-authority) configurations that together powe…
|
||||
- [table](./src/components/table/table.md) — <!-- begin:auto-docs --> _(no submodules)_- table.variants — `tableVariants` and its eight sub-…
|
||||
- [table](./src/components/table/table.md) — <!-- begin:auto-docs --> _(no submodules)_- table.variants — `tableVariants` and its eight sub-…
|
||||
- [table.variants](./src/components/table/table.variants.md) — `tableVariants` and its eight sub-component cva resolvers (`tableHeaderVariants`, `tableBodyVariants`, `tableFooterVari…
|
||||
- [tabs](./src/components/tabs/tabs.md) — <!-- begin:auto-docs --> _(no submodules)_- tabs.variants — `tabsVariants`, `tabsListVariants`,…
|
||||
- [tabs](./src/components/tabs/tabs.md) — <!-- begin:auto-docs --> _(no submodules)_- tabs.variants — `tabsVariants`, `tabsListVariants`,…
|
||||
- [tabs.variants](./src/components/tabs/tabs.variants.md) — `tabsVariants`, `tabsListVariants`, `tabsTriggerVariants`, and `tabsContentVariants` are the cva (class-variance-author…
|
||||
- [textarea](./src/components/textarea/textarea.md) — <!-- begin:auto-docs --> _(no submodules)_- textarea.variants — `textareaVariants` is the cva (…
|
||||
- [textarea](./src/components/textarea/textarea.md) — <!-- begin:auto-docs --> _(no submodules)_- textarea.variants — `textareaVariants` is the cva (…
|
||||
- [textarea.variants](./src/components/textarea/textarea.variants.md) — `textareaVariants` is the cva (class-variance-authority) configuration that powers every `<Textarea>` render. It is spl…
|
||||
- [theme-toggle](./src/components/theme-toggle/theme-toggle.md) — <!-- begin:auto-docs --> _(no submodules)_- theme-toggle.variants — Cva slot for the chassis `<…
|
||||
- [theme-toggle](./src/components/theme-toggle/theme-toggle.md) — <!-- begin:auto-docs --> _(no submodules)_- theme-toggle.variants — Cva slot for the chassis `<…
|
||||
- [theme-toggle.variants](./src/components/theme-toggle/theme-toggle.variants.md) — Cva slot for the chassis `<ThemeToggle>` icon-button. Mirrors the shape of `<Button variant="outline" size="icon">` so…
|
||||
- [toast](./src/components/toast/toast.md) — <!-- begin:auto-docs --> _(no submodules)_- [toast](./src/components/toast/toast.md) — <!-- begin:auto-docs --> _(no submodules)_- [toggle](./src/components/toggle/toggle.md) — <!-- begin:auto-docs --> _(no submodules)_- toggle.variants — `toggleVariants` is the cva (clas…
|
||||
- [toggle](./src/components/toggle/toggle.md) — <!-- begin:auto-docs --> _(no submodules)_- toggle.variants — `toggleVariants` is the cva (clas…
|
||||
- [toggle.variants](./src/components/toggle/toggle.variants.md) — `toggleVariants` is the cva (class-variance-authority) configuration powering every `<Toggle>` render. It is split from…
|
||||
- [toggle-group](./src/components/toggle-group/toggle-group.md) — <!-- begin:auto-docs --> _(no submodules)_- toggle-group.variants — `toggleGroupVariants` and `…
|
||||
- [toggle-group](./src/components/toggle-group/toggle-group.md) — <!-- begin:auto-docs --> _(no submodules)_- toggle-group.variants — `toggleGroupVariants` and `…
|
||||
- [toggle-group.variants](./src/components/toggle-group/toggle-group.variants.md) — `toggleGroupVariants` and `toggleGroupItemVariants` are the cva (class-variance-authority) configurations powering `<To…
|
||||
- [tooltip](./src/components/tooltip/tooltip.md) — <!-- begin:auto-docs --> _(no submodules)_- tooltip.variants — `tooltipContentVariants` is the…
|
||||
- [tooltip](./src/components/tooltip/tooltip.md) — <!-- begin:auto-docs --> _(no submodules)_- tooltip.variants — `tooltipContentVariants` is the…
|
||||
- [tooltip.variants](./src/components/tooltip/tooltip.variants.md) — `tooltipContentVariants` is the cva configuration powering the `<TooltipContent>` floating bubble. It is split from `to…
|
||||
- [use-toast-sonner-bridge](./src/components/use-toast-sonner-bridge/use-toast-sonner-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-toast-sonner-bridge](./src/components/use-toast-sonner-bridge/use-toast-sonner-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/errors/error-codes.md) — Defines every stable error code emitted by the `@suaveplan/react-ui` package. Codes are grouped under the `REACT_UI` na…
|
||||
- [react-ui-error](./src/errors/react-ui-error.md) — Owns every error thrown by the `@suaveplan/react-ui` package. Extends the project-wide `SuaveplanError` so each instan…
|
||||
- [use-mobile](./src/hooks/use-mobile/use-mobile.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-mobile](./src/hooks/use-mobile/use-mobile.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-toast](./src/hooks/use-toast/use-toast.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-toast](./src/hooks/use-toast/use-toast.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-cell](./src/i18n/formatted-cell/formatted-cell.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-cell](./src/i18n/formatted-cell/formatted-cell.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-currency](./src/i18n/formatted-currency/formatted-currency.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-currency](./src/i18n/formatted-currency/formatted-currency.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-date](./src/i18n/formatted-date/formatted-date.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-date](./src/i18n/formatted-date/formatted-date.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-list](./src/i18n/formatted-list/formatted-list.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-list](./src/i18n/formatted-list/formatted-list.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-message](./src/i18n/formatted-message/formatted-message.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-message](./src/i18n/formatted-message/formatted-message.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-number](./src/i18n/formatted-number/formatted-number.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-number](./src/i18n/formatted-number/formatted-number.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-relative-time](./src/i18n/formatted-relative-time/formatted-relative-time.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-relative-time](./src/i18n/formatted-relative-time/formatted-relative-time.md) — <!-- begin:auto-docs --> _(no submodules)_- [plural](./src/i18n/plural/plural.md) — <!-- begin:auto-docs --> _(no submodules)_- [plural](./src/i18n/plural/plural.md) — <!-- begin:auto-docs --> _(no submodules)_- [react-ui-i18n-bridge](./src/i18n/react-ui-i18n-bridge/react-ui-i18n-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [react-ui-i18n-bridge](./src/i18n/react-ui-i18n-bridge/react-ui-i18n-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [stories-shell](./src/i18n/stories-shell/stories-shell.md) — <!-- begin:auto-docs --> _(no submodules)_- [stories-shell](./src/i18n/stories-shell/stories-shell.md) — <!-- begin:auto-docs --> _(no submodules)_- [tanstack-bridge](./src/i18n/tanstack-bridge/tanstack-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [tanstack-bridge](./src/i18n/tanstack-bridge/tanstack-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [validation-message-bridge](./src/i18n/validation-message-bridge/validation-message-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [validation-message-bridge](./src/i18n/validation-message-bridge/validation-message-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [augmentation](./src/locales/augmentation.md) — Side-effect-only TypeScript module that augments `@suaveplan/react-i18n`'s `ContributedTranslationKeys` interface with…
|
||||
- [schema](./src/locales/schema.md) — Zod schema asserting that every locale bundle declares exactly the keys present in the canonical `en-GB.json` source-of…
|
||||
- [translation-keys](./src/locales/translation-keys.md) — `translation-keys.ts` is the codegen-emitted string-literal union of every translation key contributed by `@suaveplan/r…
|
||||
- [observability](./src/observability/react-ui-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [react-ui-metrics](./src/observability/react-ui-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [pack](./src/pack/pack-context.md) — <!-- begin:auto-docs --> _(no submodules)_- pack-registry-provider — <!-- begin:auto-docs --> _…
|
||||
- [pack-registry-provider](./src/pack/pack-registry-provider/pack-registry-provider.md) — <!-- begin:auto-docs --> _(no submodules)_- [pack-registry-provider](./src/pack/pack-registry-provider/pack-registry-provider.md) — <!-- begin:auto-docs --> _(no submodules)_- [runtime-override](./src/pack/runtime-override/runtime-override.md) — <!-- begin:auto-docs --> _(no submodules)_- [runtime-override](./src/pack/runtime-override/runtime-override.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-pack-component](./src/pack/use-pack-component/use-pack-component.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-pack-component](./src/pack/use-pack-component/use-pack-component.md) — <!-- begin:auto-docs --> _(no submodules)_- [version-check](./src/pack/version-check/version-check.md) — <!-- begin:auto-docs --> _(no submodules)_- [version-check](./src/pack/version-check/version-check.md) — <!-- begin:auto-docs --> _(no submodules)_- [pack-context](./src/pack/pack-context.md) — <!-- begin:auto-docs --> _(no submodules)_- pack-registry-provider — <!-- begin:auto-docs --> _…
|
||||
- [react-ui-provider](./src/provider/react-ui-provider/react-ui-provider.md) — <!-- begin:auto-docs --> _(no submodules)_- [react-ui-provider](./src/provider/react-ui-provider/react-ui-provider.md) — <!-- begin:auto-docs --> _(no submodules)_- [theme](./src/theme/theme-context.md) — <!-- begin:auto-docs --> _(no submodules)_- theme-provider — <!-- begin:auto-docs --> _(no subm…
|
||||
- [theme-provider](./src/theme/theme-provider/theme-provider.md) — <!-- begin:auto-docs --> _(no submodules)_- [theme-provider](./src/theme/theme-provider/theme-provider.md) — <!-- begin:auto-docs --> _(no submodules)_- [theme-switcher](./src/theme/theme-switcher/theme-switcher.md) — <!-- begin:auto-docs --> _(no submodules)_- [theme-switcher](./src/theme/theme-switcher/theme-switcher.md) — <!-- begin:auto-docs --> _(no submodules)_- [dark](./src/theme/themes/dark.md) — The dark counterpart of `lightTheme`. Same variant surface, same token names, lightness anchors inverted so foreground…
|
||||
- [high-contrast](./src/theme/themes/high-contrast.md) — Accessibility-first theme that meets WCAG AAA contrast for normal and large text alike. Pure-white foreground (`#FFFFFF…
|
||||
- [light](./src/theme/themes/light.md) — The default `ThemePlugin` instance shipped with `@suaveplan/react-ui`. `lightTheme` matches the shadcn `nova` token pre…
|
||||
- [use-theme](./src/theme/use-theme/use-theme.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-theme](./src/theme/use-theme/use-theme.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-theme-tokens](./src/theme/use-theme-tokens/use-theme-tokens.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-theme-tokens](./src/theme/use-theme-tokens/use-theme-tokens.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-themed-variants](./src/theme/use-themed-variants/use-themed-variants.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-themed-variants](./src/theme/use-themed-variants/use-themed-variants.md) — <!-- begin:auto-docs --> _(no submodules)_- [theme-context](./src/theme/theme-context.md) — <!-- begin:auto-docs --> _(no submodules)_- theme-provider — <!-- begin:auto-docs --> _(no subm…
|
||||
- [cn](./src/utils/cn/cn.md) — <!-- begin:auto-docs --> _(no submodules)_- [cn](./src/utils/cn/cn.md) — <!-- begin:auto-docs --> _(no submodules)_- [motion](./src/utils/motion/motion.md) — <!-- begin:auto-docs --> _(no submodules)_- [motion](./src/utils/motion/motion.md) — <!-- begin:auto-docs --> _(no submodules)_- [variants](./src/variants/variants.md) — <!-- begin:auto-docs --> _(no submodules)_- [variants](./src/variants/variants.md) — <!-- begin:auto-docs --> _(no submodules)_- [version](./src/version.md) — Declares the running version of `@suaveplan/react-ui` as the constant `REACT_UI_VERSION`. The version is hand-maintaine…
|
||||
- [accordion](./src/components/accordion/accordion.md) — <!-- begin:auto-docs --> _(no submodules)_- [accordion.variants](./src/components/accordion/accordion.variants.md) — `accordionVariants`, `accordionItemVariants`, `accordionHeaderVariants`, `accordionTriggerVariants`, and `accordionCont…
|
||||
- [alert](./src/components/alert/alert.md) — <!-- begin:auto-docs --> _(no submodules)_ - [alert](./src/components/alert/alert.md) — <!-- begin:auto-docs --> _(no submodules)_ - [alert.variants](./src/components/alert/alert.variants.md) — `alertVariants` is the cva config that powers the `<Alert>` chassis component. It exposes a `variant` axis with four ru…
|
||||
- [alert-dialog](./src/components/alert-dialog/alert-dialog.md) — <!-- begin:auto-docs --> _(no submodules)_ - [alert-dialog](./src/components/alert-dialog/alert-dialog.md) — <!-- begin:auto-docs --> _(no submodules)_ - [alert-dialog.variants](./src/components/alert-dialog/alert-dialog.variants.md) — `alertDialogOverlayVariants`, `alertDialogContentVariants`, `alertDialogHeaderVariants`, `alertDialogFooterVariants`, `…
|
||||
- [aspect-ratio](./src/components/aspect-ratio/aspect-ratio.md) — <!-- begin:auto-docs --> _(no submodules)_ - [aspect-ratio](./src/components/aspect-ratio/aspect-ratio.md) — <!-- begin:auto-docs --> _(no submodules)_ - [aspect-ratio.variants](./src/components/aspect-ratio/aspect-ratio.variants.md) — `aspectRatioVariants` is the cva (class-variance-authority) configuration powering the `<AspectRatio>` outer wrapper. I…
|
||||
- [avatar](./src/components/avatar/avatar.md) — <!-- begin:auto-docs --> _(no submodules)_ - [avatar](./src/components/avatar/avatar.md) — <!-- begin:auto-docs --> _(no submodules)_ - [avatar.variants](./src/components/avatar/avatar.variants.md) — `avatarVariants` is the cva config that powers the `<Avatar>` chassis component (`<AvatarImage>` and `<AvatarFallback>`…
|
||||
- [badge](./src/components/badge/badge.md) — <!-- begin:auto-docs --> _(no submodules)_ - [badge](./src/components/badge/badge.md) — <!-- begin:auto-docs --> _(no submodules)_ - [badge.variants](./src/components/badge/badge.variants.md) — `badgeVariants` is the cva config that powers the `<Badge>` chassis component. It exposes a `variant` axis with four ru…
|
||||
- [breadcrumb](./src/components/breadcrumb/breadcrumb.md) — <!-- begin:auto-docs --> _(no submodules)_ - [breadcrumb](./src/components/breadcrumb/breadcrumb.md) — <!-- begin:auto-docs --> _(no submodules)_ - [breadcrumb.variants](./src/components/breadcrumb/breadcrumb.variants.md) — `breadcrumbVariants`, `breadcrumbListVariants`, `breadcrumbItemVariants`, `breadcrumbLinkVariants`, `breadcrumbPageVari…
|
||||
- [button](./src/components/button/button.md) — <!-- begin:auto-docs --> _(no submodules)_ - [button](./src/components/button/button.md) — <!-- begin:auto-docs --> _(no submodules)_ - [button.variants](./src/components/button/button.variants.md) — `buttonVariants` is the cva (class-variance-authority) configuration that powers every `<Button>` render. It is split f…
|
||||
- [calendar](./src/components/calendar/calendar.md) — <!-- begin:auto-docs --> _(no submodules)_ - [calendar](./src/components/calendar/calendar.md) — <!-- begin:auto-docs --> _(no submodules)_ - [calendar.test-fixtures](./src/components/calendar/calendar.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.calendar.*` keyset. The chas…
|
||||
- [calendar.variants](./src/components/calendar/calendar.variants.md) — `calendarVariants` is the cva config that powers the outer wrapper of `<Calendar>`. The recipe is intentionally minimal…
|
||||
- [card](./src/components/card/card.md) — <!-- begin:auto-docs --> _(no submodules)_ - [card](./src/components/card/card.md) — <!-- begin:auto-docs --> _(no submodules)_ - [card.variants](./src/components/card/card.variants.md) — `cardVariants` (and its sub-component cva resolvers `cardHeaderVariants`, `cardTitleVariants`, `cardDescriptionVariants…
|
||||
- [checkbox](./src/components/checkbox/checkbox.md) — <!-- begin:auto-docs --> _(no submodules)_ - [checkbox](./src/components/checkbox/checkbox.md) — <!-- begin:auto-docs --> _(no submodules)_ - [checkbox.variants](./src/components/checkbox/checkbox.variants.md) — `checkboxVariants` is the cva (class-variance-authority) configuration that powers every `<Checkbox>` render. It is spl…
|
||||
- [collapsible](./src/components/collapsible/collapsible.md) — <!-- begin:auto-docs --> _(no submodules)_ - [collapsible](./src/components/collapsible/collapsible.md) — <!-- begin:auto-docs --> _(no submodules)_ - [collapsible.variants](./src/components/collapsible/collapsible.variants.md) — `collapsibleVariants`, `collapsibleTriggerVariants`, and `collapsibleContentVariants` are the cva (class-variance-autho…
|
||||
- [confirm-delete-dialog](./src/components/confirm-delete-dialog/confirm-delete-dialog.md) — <!-- begin:auto-docs --> _(no submodules)_ - [confirm-delete-dialog](./src/components/confirm-delete-dialog/confirm-delete-dialog.md) — <!-- begin:auto-docs --> _(no submodules)_ - [confirm-delete-dialog.test-fixtures](./src/components/confirm-delete-dialog/confirm-delete-dialog.test-fixtures.md) — Wave-3g local test fixture that augments the chassis test i18n instance with the `react-ui.confirmDelete.*` keyset. The…
|
||||
- [context-menu](./src/components/context-menu/context-menu.md) — <!-- begin:auto-docs --> _(no submodules)_ - [context-menu](./src/components/context-menu/context-menu.md) — <!-- begin:auto-docs --> _(no submodules)_ - [context-menu.variants](./src/components/context-menu/context-menu.variants.md) — `contextMenuContentVariants`, `contextMenuItemVariants`, `contextMenuLabelVariants`, `contextMenuSeparatorVariants`, `c…
|
||||
- [data-table](./src/components/data-table/data-table.md) — <!-- begin:auto-docs --> _(no submodules)_ - [data-table](./src/components/data-table/data-table.md) — <!-- begin:auto-docs --> _(no submodules)_ - [data-table.variants](./src/components/data-table/data-table.variants.md) — `dataTableVariants`, `dataTablePaginationVariants`, and `dataTableSortHeaderVariants` drive the chassis `<DataTable>` o…
|
||||
- [dialog](./src/components/dialog/dialog.md) — <!-- begin:auto-docs --> _(no submodules)_ - [dialog](./src/components/dialog/dialog.md) — <!-- begin:auto-docs --> _(no submodules)_ - [dialog.variants](./src/components/dialog/dialog.variants.md) — `dialogOverlayVariants`, `dialogContentVariants`, `dialogHeaderVariants`, `dialogFooterVariants`, `dialogTitleVariants`…
|
||||
- [drawer](./src/components/drawer/drawer.md) — <!-- begin:auto-docs --> _(no submodules)_ - [drawer](./src/components/drawer/drawer.md) — <!-- begin:auto-docs --> _(no submodules)_ - [drawer.variants](./src/components/drawer/drawer.variants.md) — `drawerOverlayVariants`, `drawerContentVariants`, `drawerHandleVariants`, `drawerHeaderVariants`, `drawerFooterVariants…
|
||||
- [dropdown-menu](./src/components/dropdown-menu/dropdown-menu.md) — <!-- begin:auto-docs --> _(no submodules)_ - [dropdown-menu](./src/components/dropdown-menu/dropdown-menu.md) — <!-- begin:auto-docs --> _(no submodules)_ - [dropdown-menu.variants](./src/components/dropdown-menu/dropdown-menu.variants.md) — `dropdownMenuContentVariants`, `dropdownMenuItemVariants`, `dropdownMenuLabelVariants`, `dropdownMenuSeparatorVariants`…
|
||||
- [empty](./src/components/empty/empty.md) — <!-- begin:auto-docs --> _(no submodules)_ - [empty](./src/components/empty/empty.md) — <!-- begin:auto-docs --> _(no submodules)_ - [empty.variants](./src/components/empty/empty.variants.md) — `emptyVariants` is the cva config that powers the `<Empty>` chassis component (with `emptyHeaderVariants`, `emptyMediaV…
|
||||
- [error-boundary](./src/components/error-boundary/error-boundary.md) — <!-- begin:auto-docs --> _(no submodules)_ - [error-boundary](./src/components/error-boundary/error-boundary.md) — <!-- begin:auto-docs --> _(no submodules)_ - [error-boundary.metrics](./src/components/error-boundary/error-boundary.metrics.md) — Co-located OTel counter for the chassis `<ErrorBoundary>` component. Lives next to the boundary itself rather than in t…
|
||||
- [error-boundary.test-fixtures](./src/components/error-boundary/error-boundary.test-fixtures.md) — Wave-3g local test fixture that augments the chassis test i18n instance with the `react-ui.errorBoundary.*` keyset. The…
|
||||
- [error-boundary.variants](./src/components/error-boundary/error-boundary.variants.md) — Cva slot definitions for the chassis `<ErrorBoundary>` default fallback. Splits the fallback into four named slots — ou…
|
||||
- [field](./src/components/field/field.md) — <!-- begin:auto-docs --> _(no submodules)_ - [field](./src/components/field/field.md) — <!-- begin:auto-docs --> _(no submodules)_ - [field.variants](./src/components/field/field.variants.md) — `fieldVariants`, `fieldLabelVariants`, `fieldDescriptionVariants`, and `fieldErrorVariants` are the four cva (class-var…
|
||||
- [field-group](./src/components/field-group/field-group.md) — <!-- begin:auto-docs --> _(no submodules)_ - [field-group](./src/components/field-group/field-group.md) — <!-- begin:auto-docs --> _(no submodules)_ - [field-group.variants](./src/components/field-group/field-group.variants.md) — `fieldGroupVariants` is the cva (class-variance-authority) configuration powering every `<FieldGroup>` render. It is sp…
|
||||
- [field-set](./src/components/field-set/field-set.md) — <!-- begin:auto-docs --> _(no submodules)_ - [field-set](./src/components/field-set/field-set.md) — <!-- begin:auto-docs --> _(no submodules)_ - [field-set.variants](./src/components/field-set/field-set.variants.md) — `fieldSetVariants` and `fieldLegendVariants` are the cva (class-variance-authority) configurations powering `<FieldSet>…
|
||||
- [hover-card](./src/components/hover-card/hover-card.md) — <!-- begin:auto-docs --> _(no submodules)_ - [hover-card](./src/components/hover-card/hover-card.md) — <!-- begin:auto-docs --> _(no submodules)_ - [hover-card.variants](./src/components/hover-card/hover-card.variants.md) — `hoverCardContentVariants` is the cva configuration powering the `<HoverCardContent>` floating panel. It is split from…
|
||||
- [input](./src/components/input/input.md) — <!-- begin:auto-docs --> _(no submodules)_ - [input-number](./src/components/input/input-number/input-number.md) — <!-- begin:auto-docs --> _(no submodules)_ - [input-number](./src/components/input/input-number/input-number.md) — <!-- begin:auto-docs --> _(no submodules)_ - [input](./src/components/input/input.md) — <!-- begin:auto-docs --> _(no submodules)_ - [input.variants](./src/components/input/input.variants.md) — `inputVariants` is the cva (class-variance-authority) configuration that powers every `<Input>` render. It is split fro…
|
||||
- [input-group](./src/components/input-group/input-group.md) — <!-- begin:auto-docs --> _(no submodules)_ - [input-group](./src/components/input-group/input-group.md) — <!-- begin:auto-docs --> _(no submodules)_ - [input-group.variants](./src/components/input-group/input-group.variants.md) — `inputGroupVariants`, `inputGroupAddonVariants`, `inputGroupInputVariants`, `inputGroupTextareaVariants`, and `inputGro…
|
||||
- [label](./src/components/label/label.md) — <!-- begin:auto-docs --> _(no submodules)_ - [label](./src/components/label/label.md) — <!-- begin:auto-docs --> _(no submodules)_ - [label.variants](./src/components/label/label.variants.md) — `labelVariants` is the cva (class-variance-authority) configuration that powers every `<Label>` render. It is split fro…
|
||||
- [menubar](./src/components/menubar/menubar.md) — <!-- begin:auto-docs --> _(no submodules)_ - [menubar](./src/components/menubar/menubar.md) — <!-- begin:auto-docs --> _(no submodules)_ - [menubar.variants](./src/components/menubar/menubar.variants.md) — `menubarVariants`, `menubarTriggerVariants`, `menubarContentVariants`, `menubarItemVariants`, `menubarLabelVariants`, `…
|
||||
- [pagination](./src/components/pagination/pagination.md) — <!-- begin:auto-docs --> _(no submodules)_ - [pagination](./src/components/pagination/pagination.md) — <!-- begin:auto-docs --> _(no submodules)_ - [pagination.variants](./src/components/pagination/pagination.variants.md) — `paginationVariants`, `paginationContentVariants`, `paginationItemVariants`, `paginationLinkVariants`, `paginationPrevN…
|
||||
- [popover](./src/components/popover/popover.md) — <!-- begin:auto-docs --> _(no submodules)_ - [popover](./src/components/popover/popover.md) — <!-- begin:auto-docs --> _(no submodules)_ - [popover.variants](./src/components/popover/popover.variants.md) — `popoverContentVariants` is the cva configuration powering the `<PopoverContent>` floating panel. It is split from `pop…
|
||||
- [progress](./src/components/progress/progress.md) — <!-- begin:auto-docs --> _(no submodules)_ - [progress](./src/components/progress/progress.md) — <!-- begin:auto-docs --> _(no submodules)_ - [progress.variants](./src/components/progress/progress.variants.md) — `progressVariants` is the cva config that powers the `<Progress>` chassis component (paired with `progressIndicatorVari…
|
||||
- [radio-group](./src/components/radio-group/radio-group.md) — <!-- begin:auto-docs --> _(no submodules)_ - [radio-group](./src/components/radio-group/radio-group.md) — <!-- begin:auto-docs --> _(no submodules)_ - [radio-group.variants](./src/components/radio-group/radio-group.variants.md) — `radioGroupVariants` and `radioGroupItemVariants` are the cva (class-variance-authority) configurations powering `<Radi…
|
||||
- [resizable](./src/components/resizable/resizable.md) — <!-- begin:auto-docs --> _(no submodules)_ - [resizable](./src/components/resizable/resizable.md) — <!-- begin:auto-docs --> _(no submodules)_ - [resizable.variants](./src/components/resizable/resizable.variants.md) — `resizablePanelGroupVariants`, `resizablePanelVariants`, `resizableHandleVariants`, and `resizableHandleGripVariants` a…
|
||||
- [scroll-area](./src/components/scroll-area/scroll-area.md) — <!-- begin:auto-docs --> _(no submodules)_ - [scroll-area](./src/components/scroll-area/scroll-area.md) — <!-- begin:auto-docs --> _(no submodules)_ - [scroll-area.variants](./src/components/scroll-area/scroll-area.variants.md) — `scrollAreaVariants`, `scrollAreaViewportVariants`, `scrollAreaScrollbarVariants`, `scrollAreaThumbVariants`, and `scro…
|
||||
- [select](./src/components/select/select.md) — <!-- begin:auto-docs --> _(no submodules)_ - [select](./src/components/select/select.md) — <!-- begin:auto-docs --> _(no submodules)_ - [select.test-fixtures](./src/components/select/select.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.select.placeholder` key. The…
|
||||
- [select.variants](./src/components/select/select.variants.md) — The `<Select>` component is composed of six visual slots — trigger, content panel, item row, group label, separator, an…
|
||||
- [separator](./src/components/separator/separator.md) — <!-- begin:auto-docs --> _(no submodules)_ - [separator](./src/components/separator/separator.md) — <!-- begin:auto-docs --> _(no submodules)_ - [separator.variants](./src/components/separator/separator.variants.md) — `separatorVariants` is the cva (class-variance-authority) configuration that powers every `<Separator>` render. It is s…
|
||||
- [sheet](./src/components/sheet/sheet.md) — <!-- begin:auto-docs --> _(no submodules)_ - [sheet](./src/components/sheet/sheet.md) — <!-- begin:auto-docs --> _(no submodules)_ - [sheet.variants](./src/components/sheet/sheet.variants.md) — `sheetOverlayVariants`, `sheetContentVariants`, `sheetHeaderVariants`, `sheetFooterVariants`, `sheetTitleVariants`, `sh…
|
||||
- [skeleton](./src/components/skeleton/skeleton.md) — <!-- begin:auto-docs --> _(no submodules)_ - [skeleton](./src/components/skeleton/skeleton.md) — <!-- begin:auto-docs --> _(no submodules)_ - [skeleton.variants](./src/components/skeleton/skeleton.variants.md) — `skeletonVariants` is the cva (class-variance-authority) configuration that powers every `<Skeleton>` render. It is spl…
|
||||
- [sonner](./src/components/sonner/sonner.md) — <!-- begin:auto-docs --> _(no submodules)_ - [sonner](./src/components/sonner/sonner.md) — <!-- begin:auto-docs --> _(no submodules)_ - [sonner.variants](./src/components/sonner/sonner.variants.md) — Defines the `sonnerToasterVariants` cva slot for the chassis `<Sonner>` toaster host. Sonner manages its own stacking,…
|
||||
- [spinner](./src/components/spinner/spinner.md) — <!-- begin:auto-docs --> _(no submodules)_ - [spinner](./src/components/spinner/spinner.md) — <!-- begin:auto-docs --> _(no submodules)_ - [spinner.variants](./src/components/spinner/spinner.variants.md) — `spinnerVariants` is the cva config that powers the `<Spinner>` chassis component. It exposes a `size` axis (`sm` / `de…
|
||||
- [status-badge](./src/components/status-badge/status-badge.md) — <!-- begin:auto-docs --> _(no submodules)_ - [status-badge](./src/components/status-badge/status-badge.md) — <!-- begin:auto-docs --> _(no submodules)_ - [status-badge.variants](./src/components/status-badge/status-badge.variants.md) — `statusBadgeVariants` is the cva config that powers the `<StatusBadge>` chassis component. It exposes a `status` axis w…
|
||||
- [switch](./src/components/switch/switch.md) — <!-- begin:auto-docs --> _(no submodules)_ - [switch](./src/components/switch/switch.md) — <!-- begin:auto-docs --> _(no submodules)_ - [switch.variants](./src/components/switch/switch.variants.md) — `switchVariants` and `switchThumbVariants` are the two cva (class-variance-authority) configurations that together powe…
|
||||
- [table](./src/components/table/table.md) — <!-- begin:auto-docs --> _(no submodules)_ - [table](./src/components/table/table.md) — <!-- begin:auto-docs --> _(no submodules)_ - [table.variants](./src/components/table/table.variants.md) — `tableVariants` and its eight sub-component cva resolvers (`tableHeaderVariants`, `tableBodyVariants`, `tableFooterVari…
|
||||
- [tabs](./src/components/tabs/tabs.md) — <!-- begin:auto-docs --> _(no submodules)_ - [tabs](./src/components/tabs/tabs.md) — <!-- begin:auto-docs --> _(no submodules)_ - [tabs.variants](./src/components/tabs/tabs.variants.md) — `tabsVariants`, `tabsListVariants`, `tabsTriggerVariants`, and `tabsContentVariants` are the cva (class-variance-author…
|
||||
- [textarea](./src/components/textarea/textarea.md) — <!-- begin:auto-docs --> _(no submodules)_ - [textarea](./src/components/textarea/textarea.md) — <!-- begin:auto-docs --> _(no submodules)_ - [textarea.variants](./src/components/textarea/textarea.variants.md) — `textareaVariants` is the cva (class-variance-authority) configuration that powers every `<Textarea>` render. It is spl…
|
||||
- [theme-toggle](./src/components/theme-toggle/theme-toggle.md) — <!-- begin:auto-docs --> _(no submodules)_ - [theme-toggle](./src/components/theme-toggle/theme-toggle.md) — <!-- begin:auto-docs --> _(no submodules)_ - [theme-toggle.variants](./src/components/theme-toggle/theme-toggle.variants.md) — Cva slot for the chassis `<ThemeToggle>` icon-button. Mirrors the shape of `<Button variant="outline" size="icon">` so…
|
||||
- [toast](./src/components/toast/toast.md) — <!-- begin:auto-docs --> _(no submodules)_ - [toast](./src/components/toast/toast.md) — <!-- begin:auto-docs --> _(no submodules)_- [toggle](./src/components/toggle/toggle.md) — <!-- begin:auto-docs --> _(no submodules)_ - [toggle](./src/components/toggle/toggle.md) — <!-- begin:auto-docs --> _(no submodules)_ - [toggle.variants](./src/components/toggle/toggle.variants.md) — `toggleVariants` is the cva (class-variance-authority) configuration powering every `<Toggle>` render. It is split from…
|
||||
- [toggle-group](./src/components/toggle-group/toggle-group.md) — <!-- begin:auto-docs --> _(no submodules)_ - [toggle-group](./src/components/toggle-group/toggle-group.md) — <!-- begin:auto-docs --> _(no submodules)_ - [toggle-group.variants](./src/components/toggle-group/toggle-group.variants.md) — `toggleGroupVariants` and `toggleGroupItemVariants` are the cva (class-variance-authority) configurations powering `<To…
|
||||
- [tooltip](./src/components/tooltip/tooltip.md) — <!-- begin:auto-docs --> _(no submodules)_ - [tooltip](./src/components/tooltip/tooltip.md) — <!-- begin:auto-docs --> _(no submodules)_ - [tooltip.variants](./src/components/tooltip/tooltip.variants.md) — `tooltipContentVariants` is the cva configuration powering the `<TooltipContent>` floating bubble. It is split from `to…
|
||||
- [use-toast-sonner-bridge](./src/components/use-toast-sonner-bridge/use-toast-sonner-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_ - [use-toast-sonner-bridge](./src/components/use-toast-sonner-bridge/use-toast-sonner-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/errors/error-codes.md) — Defines every stable error code emitted by the `@suaveplan/react-ui` package. Codes are grouped under the `REACT_UI` na…
|
||||
- [react-ui-error](./src/errors/react-ui-error.md) — Owns every error thrown by the `@suaveplan/react-ui` package. Extends the project-wide `SuaveplanError` so each instan…
|
||||
- [use-mobile](./src/hooks/use-mobile/use-mobile.md) — <!-- begin:auto-docs --> _(no submodules)_ - [use-mobile](./src/hooks/use-mobile/use-mobile.md) — <!-- begin:auto-docs --> _(no submodules)_- [use-toast](./src/hooks/use-toast/use-toast.md) — <!-- begin:auto-docs --> _(no submodules)_ - [use-toast](./src/hooks/use-toast/use-toast.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-cell](./src/i18n/formatted-cell/formatted-cell.md) — <!-- begin:auto-docs --> _(no submodules)_ - [formatted-cell](./src/i18n/formatted-cell/formatted-cell.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-currency](./src/i18n/formatted-currency/formatted-currency.md) — <!-- begin:auto-docs --> _(no submodules)_ - [formatted-currency](./src/i18n/formatted-currency/formatted-currency.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-date](./src/i18n/formatted-date/formatted-date.md) — <!-- begin:auto-docs --> _(no submodules)_ - [formatted-date](./src/i18n/formatted-date/formatted-date.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-list](./src/i18n/formatted-list/formatted-list.md) — <!-- begin:auto-docs --> _(no submodules)_ - [formatted-list](./src/i18n/formatted-list/formatted-list.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-message](./src/i18n/formatted-message/formatted-message.md) — <!-- begin:auto-docs --> _(no submodules)_ - [formatted-message](./src/i18n/formatted-message/formatted-message.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-number](./src/i18n/formatted-number/formatted-number.md) — <!-- begin:auto-docs --> _(no submodules)_ - [formatted-number](./src/i18n/formatted-number/formatted-number.md) — <!-- begin:auto-docs --> _(no submodules)_- [formatted-relative-time](./src/i18n/formatted-relative-time/formatted-relative-time.md) — <!-- begin:auto-docs --> _(no submodules)_ - [formatted-relative-time](./src/i18n/formatted-relative-time/formatted-relative-time.md) — <!-- begin:auto-docs --> _(no submodules)_- [plural](./src/i18n/plural/plural.md) — <!-- begin:auto-docs --> _(no submodules)_ - [plural](./src/i18n/plural/plural.md) — <!-- begin:auto-docs --> _(no submodules)_- [react-ui-i18n-bridge](./src/i18n/react-ui-i18n-bridge/react-ui-i18n-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_ - [react-ui-i18n-bridge](./src/i18n/react-ui-i18n-bridge/react-ui-i18n-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [stories-shell](./src/i18n/stories-shell/stories-shell.md) — <!-- begin:auto-docs --> _(no submodules)_ - [stories-shell](./src/i18n/stories-shell/stories-shell.md) — <!-- begin:auto-docs --> _(no submodules)_- [tanstack-bridge](./src/i18n/tanstack-bridge/tanstack-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_ - [tanstack-bridge](./src/i18n/tanstack-bridge/tanstack-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [validation-message-bridge](./src/i18n/validation-message-bridge/validation-message-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_ - [validation-message-bridge](./src/i18n/validation-message-bridge/validation-message-bridge.md) — <!-- begin:auto-docs --> _(no submodules)_- [augmentation](./src/locales/augmentation.md) — Side-effect-only TypeScript module that augments `@suaveplan/react-i18n`'s `ContributedTranslationKeys` interface with…
|
||||
- [schema](./src/locales/schema.md) — Zod schema asserting that every locale bundle declares exactly the keys present in the canonical `en-GB.json` source-of…
|
||||
- [translation-keys](./src/locales/translation-keys.md) — `translation-keys.ts` is the codegen-emitted string-literal union of every translation key contributed by `@suaveplan/r…
|
||||
- [observability](./src/observability/react-ui-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_ - [react-ui-metrics](./src/observability/react-ui-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [pack](./src/pack/pack-context.md) — <!-- begin:auto-docs --> _(no submodules)_ - [pack-registry-provider](./src/pack/pack-registry-provider/pack-registry-provider.md) — <!-- begin:auto-docs --> _(no submodules)_ - [pack-registry-provider](./src/pack/pack-registry-provider/pack-registry-provider.md) — <!-- begin:auto-docs --> _(no submodules)_ - [runtime-override](./src/pack/runtime-override/runtime-override.md) — <!-- begin:auto-docs --> _(no submodules)_ - [runtime-override](./src/pack/runtime-override/runtime-override.md) — <!-- begin:auto-docs --> _(no submodules)_ - [use-pack-component](./src/pack/use-pack-component/use-pack-component.md) — <!-- begin:auto-docs --> _(no submodules)_ - [use-pack-component](./src/pack/use-pack-component/use-pack-component.md) — <!-- begin:auto-docs --> _(no submodules)_ - [version-check](./src/pack/version-check/version-check.md) — <!-- begin:auto-docs --> _(no submodules)_ - [version-check](./src/pack/version-check/version-check.md) — <!-- begin:auto-docs --> _(no submodules)_ - [pack-context](./src/pack/pack-context.md) — <!-- begin:auto-docs --> _(no submodules)_- [react-ui-provider](./src/provider/react-ui-provider/react-ui-provider.md) — <!-- begin:auto-docs --> _(no submodules)_ - [react-ui-provider](./src/provider/react-ui-provider/react-ui-provider.md) — <!-- begin:auto-docs --> _(no submodules)_- [theme](./src/theme/theme-context.md) — <!-- begin:auto-docs --> _(no submodules)_ - [theme-provider](./src/theme/theme-provider/theme-provider.md) — <!-- begin:auto-docs --> _(no submodules)_ - [theme-provider](./src/theme/theme-provider/theme-provider.md) — <!-- begin:auto-docs --> _(no submodules)_ - [theme-switcher](./src/theme/theme-switcher/theme-switcher.md) — <!-- begin:auto-docs --> _(no submodules)_ - [theme-switcher](./src/theme/theme-switcher/theme-switcher.md) — <!-- begin:auto-docs --> _(no submodules)_ - [dark](./src/theme/themes/dark.md) — The dark counterpart of `lightTheme`. Same variant surface, same token names, lightness anchors inverted so foreground…
|
||||
- [high-contrast](./src/theme/themes/high-contrast.md) — Accessibility-first theme that meets WCAG AAA contrast for normal and large text alike. Pure-white foreground (`#FFFFFF…
|
||||
- [light](./src/theme/themes/light.md) — The default `ThemePlugin` instance shipped with `@suaveplan/react-ui`. `lightTheme` matches the shadcn `nova` token pre…
|
||||
- [use-theme](./src/theme/use-theme/use-theme.md) — <!-- begin:auto-docs --> _(no submodules)_ - [use-theme](./src/theme/use-theme/use-theme.md) — <!-- begin:auto-docs --> _(no submodules)_ - [use-theme-tokens](./src/theme/use-theme-tokens/use-theme-tokens.md) — <!-- begin:auto-docs --> _(no submodules)_ - [use-theme-tokens](./src/theme/use-theme-tokens/use-theme-tokens.md) — <!-- begin:auto-docs --> _(no submodules)_ - [use-themed-variants](./src/theme/use-themed-variants/use-themed-variants.md) — <!-- begin:auto-docs --> _(no submodules)_ - [use-themed-variants](./src/theme/use-themed-variants/use-themed-variants.md) — <!-- begin:auto-docs --> _(no submodules)_ - [theme-context](./src/theme/theme-context.md) — <!-- begin:auto-docs --> _(no submodules)_- [cn](./src/utils/cn/cn.md) — <!-- begin:auto-docs --> _(no submodules)_ - [cn](./src/utils/cn/cn.md) — <!-- begin:auto-docs --> _(no submodules)_- [motion](./src/utils/motion/motion.md) — <!-- begin:auto-docs --> _(no submodules)_ - [motion](./src/utils/motion/motion.md) — <!-- begin:auto-docs --> _(no submodules)_- [variants](./src/variants/variants.md) — <!-- begin:auto-docs --> _(no submodules)_ - [variants](./src/variants/variants.md) — <!-- begin:auto-docs --> _(no submodules)_- [version](./src/version.md) — Declares the running version of `@suaveplan/react-ui` as the constant `REACT_UI_VERSION`. The version is hand-maintaine…
|
||||
|
||||
## What is here
|
||||
|
||||
- `<ReactUiProvider>` — opinionated wrapper composing
|
||||
`<TranslationProvider>` → `<ThemeProvider>` → `<PackRegistryProvider>`
|
||||
- `<ThemeProvider>` — theme registry with FOUC-prevention inline script,
|
||||
`prefers-color-scheme` + `prefers-reduced-motion` listeners, and a
|
||||
schema-validated `ThemePlugin` contract
|
||||
- `<PackRegistryProvider>` — plugin registry with collision detection,
|
||||
lifecycle hooks, peer-version checks (warn-only), and per-pack
|
||||
translation auto-registration
|
||||
- `useTheme`, `useThemeTokens`, `useThemedVariants`, `<ThemeSwitcher>` —
|
||||
read-side hooks
|
||||
- `usePackComponent` — typed pack-component lookup
|
||||
- `useMobile`, `useToast`, `useDirection` — chassis hooks
|
||||
- `cn`, `motionToken`, `defineVariants`, `cva` — utilities + variant
|
||||
authoring helpers
|
||||
- Three built-in themes: `lightTheme` (shadcn `nova`), `darkTheme` (pure
|
||||
inversion), `highContrastTheme` (WCAG AAA)
|
||||
- Five bundled locale catalogs (`en-GB`, `zh-CN`, `ar-AE`, `cy-GB`,
|
||||
`ru-RU`); auto-registered into the active i18n instance
|
||||
- OpenTelemetry counters / histograms / spans for every chassis
|
||||
surface (theme switches, pack lifecycle, locale-bundle loads, themed
|
||||
variants resolve, toast notifications, provider mount)
|
||||
- shadcn-CLI-compatible registry generator at
|
||||
`scripts/build-registry.ts`
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
bun add @suaveplan/react-ui @suaveplan/react-i18n @suaveplan/i18n
|
||||
```
|
||||
|
||||
## Quickstart
|
||||
|
||||
```tsx
|
||||
import { ReactUiProvider } from "@suaveplan/react-ui/provider";
|
||||
import { darkTheme, lightTheme } from "@suaveplan/react-ui/theme";
|
||||
import { createI18n } from "@suaveplan/i18n/plugin";
|
||||
|
||||
const i18n = createI18n({ defaultLocale: "en-GB" });
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
<ReactUiProvider
|
||||
i18n={i18n}
|
||||
themes={[lightTheme, darkTheme]}
|
||||
defaultTheme="light"
|
||||
>
|
||||
<YourApp />
|
||||
</ReactUiProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Tier and tree-shaking
|
||||
|
||||
- **Tier**: React. Peer-deps `react ^18.0.0 || ^19.0.0` /
|
||||
`react-dom ^18.0.0 || ^19.0.0`.
|
||||
- **Tree-shaken**: each feature module is exported under its own
|
||||
subpath in `package.json#exports` so a consumer importing only `cn`
|
||||
(`import { cn } from '@suaveplan/react-ui/utils/cn'`) does NOT pull
|
||||
the rest of the chassis.
|
||||
- **CSS side-effects**: `package.json#sideEffects` whitelists
|
||||
`styles/global.css` and the bundled locale `*.json` so bundlers
|
||||
preserve CSS layer registration AND lazy-import chunk emission.
|
||||
|
||||
## OpenSpec
|
||||
|
||||
Owning change: [`enterprise-react-ui-v2`](../../../docs/design/enterprise-react-ui-v2).
|
||||
Cross-cutting design: `docs/cross-cutting-designs/2026-05-05-react-ui-react-i18n-react-forms.md`.
|
||||
|
||||
## License
|
||||
|
||||
MIT — © Suaveplan Team.
|
||||
|
||||
77
docs/ui/react/react-ui/scripts/check-i18n-keys.md
Normal file
77
docs/ui/react/react-ui/scripts/check-i18n-keys.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# `check-i18n-keys`
|
||||
|
||||
## Purpose
|
||||
|
||||
Translation-key drift gate for `@suaveplan/react-ui`. Walks every `*.{ts,tsx}` under the package's source roots, extracts every `react-ui.*` translation-key reference from the documented call shapes, and diffs the discovered set against the canonical `src/locales/en-GB.json`. Reports `MISSING <key>` for keys referenced in code but absent from the bundle (consumers will hit a missing-key warning) and `ORPHAN <key>` for bundle keys nothing in source references (dead translation keys). Catches drift the moment a developer renames a key in code but forgets the bundle, or vice versa.
|
||||
|
||||
## Features
|
||||
|
||||
- Pattern-based extraction over six chassis-recognised call shapes: `t("react-ui.…")`, `tDynamic(...)`, `i18nKey="…"` JSX attribute, `i18nKey: "…"` object literal, `<name>Key={"…"}` prop default, `?? "react-ui.…"` fallback shorthand, `const DEFAULT_*_KEY = "…"` module-scope literal, `labelKey: "…"` plugin shape.
|
||||
- Source location (`<file>:<line>`) attached to every MISSING diagnostic for quick navigation.
|
||||
- `--strict` flag promotes ORPHAN diagnostics to errors. Default exits 0 on ORPHAN-only (warn) so the gate ships green even when the bundle has pre-existing dead keys; CI can opt into strict mode after a cleanup pass.
|
||||
- Wired into `bunx turbo lint` — every commit in CI runs the default mode.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```bash
|
||||
bun run check:i18n-keys
|
||||
# check-i18n-keys: clean — 23 keys in code, 23 keys in bundle.
|
||||
```
|
||||
|
||||
When a key is missing:
|
||||
|
||||
```bash
|
||||
bun run check:i18n-keys
|
||||
# MISSING react-ui.foo.absent at src/components/foo/foo.tsx:42
|
||||
# check-i18n-keys: 1 missing, 0 orphan (orphan = warn)
|
||||
# (exit 1)
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
Strict mode for a periodic bundle-cleanup pass:
|
||||
|
||||
```bash
|
||||
bun run check:i18n-keys:strict
|
||||
# ORPHAN react-ui.popover.close
|
||||
# ORPHAN react-ui.alertDialog.cancel
|
||||
# check-i18n-keys: 0 missing, 2 orphan (strict)
|
||||
# (exit 1)
|
||||
```
|
||||
|
||||
The script's exported helpers can be reused by other tooling (e.g. a CI report aggregator):
|
||||
|
||||
```ts
|
||||
import {
|
||||
diff,
|
||||
extractKeysFromSource,
|
||||
loadBundleKeys,
|
||||
} from "./check-i18n-keys.js";
|
||||
|
||||
const sourceKeys = (await extractKeysFromSource(files, root)).keys;
|
||||
const bundleKeys = await loadBundleKeys(bundlePath);
|
||||
const diagnostics = diff(sourceKeys, bundleKeys);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### CLI
|
||||
|
||||
`bun scripts/check-i18n-keys.ts [--strict]`
|
||||
|
||||
- Default mode: exits non-zero only when at least one MISSING diagnostic appears. ORPHANs are printed as warnings.
|
||||
- `--strict`: exits non-zero on any diagnostic (MISSING or ORPHAN).
|
||||
|
||||
### Exported helpers
|
||||
|
||||
- `extractKeysFromSource(files, rootForRelative): Promise<{ keys: Map<key, location> }>` — walks the supplied source files, returns a map keyed by translation key, value `<relative-path>:<line>`.
|
||||
- `loadBundleKeys(bundlePath): Promise<Set<string>>` — reads the bundle JSON and returns the subset of keys in the `react-ui.*` namespace.
|
||||
- `diff(sourceKeys, bundleKeys): Diagnostic[]` — pure function returning sorted diagnostics (MISSING before ORPHAN, alphabetic within each kind).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Pattern-based instead of AST-based — no `@babel/parser` dep. The supported call shapes are a closed set documented at the top of the script. New idioms (e.g. a future `const KEYS = { close: "react-ui.foo" }` const-record style) need a new pattern; the script falls into the catch-all-of-last-resort `?? "react-ui.…"` only when the value lives in a discriminating context.
|
||||
- The extraction explicitly EXCLUDES `*.test.tsx`, `*.stories.tsx`, `*.stories.test.tsx`, and `*.bench.ts` — test fixtures and stories often reference keys that exist in the bundle for component contracts, not for the test's own runtime use; treating them as references would mask real orphans.
|
||||
- Scan roots: `src/components`, `src/i18n`, `src/theme`, `src/pack`, `src/provider`, `src/hooks`, `src/errors`. Any future surface that ships `react-ui.*` keys must be added here.
|
||||
- The default-vs-strict split is deliberate. Pre-existing orphans in the bundle (`react-ui.empty.defaultDescription`, `react-ui.popover.close`, …) are public-API keys consumers may pass via `*Key` prop overrides; removing them would be a breaking change. Default mode warns; strict mode lets the team enforce zero-orphan after a bundle cleanup change.
|
||||
- The script is a `Bun.import.meta.main` entry point; `process.exit(await main())` is the only top-level side effect. Imports are pure helpers reusable from tests.
|
||||
63
docs/ui/react/react-ui/scripts/check-registry-eject.md
Normal file
63
docs/ui/react/react-ui/scripts/check-registry-eject.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# `check-registry-eject`
|
||||
|
||||
## Purpose
|
||||
|
||||
Shadcn-eject smoke gate for `@suaveplan/react-ui`. Verifies the `registry/r/*.json` tree against the package source so a downstream consumer running `shadcn add @suaveplan/<name>` always finds the files the registry promised. Catches the realistic drift modes today: a registry entry whose listed file vanished (rename / restructure), a component directory shipped without a registry entry (forgot to regenerate), an orphaned registry entry whose component was deleted (forgot to clean up), and entries that fail JSON or shape validation. Runs as part of `bunx turbo lint` on every commit.
|
||||
|
||||
## Features
|
||||
|
||||
- Per-entry validation: shape (`name`, `type`, `files`), JSON parse, non-empty `files[]`.
|
||||
- Per-file existence: every `files[].path` resolves to a real file under `src/components/<name>/`.
|
||||
- Bidirectional drift detection: registry entries without matching component directories AND component directories without registry entries both fail.
|
||||
- Sorted, stable diagnostic output for diff-friendly CI logs.
|
||||
- Pure helper exports (`audit`, `loadEntry`) reusable from tests.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```bash
|
||||
bun run check:registry-eject
|
||||
# check-registry-eject: clean — 51 registry entries match 51 component directories.
|
||||
```
|
||||
|
||||
When a registry entry lists a missing file:
|
||||
|
||||
```bash
|
||||
bun run check:registry-eject
|
||||
# MISSING_SOURCE_FILE registry/r/toast.json: file 'toast.tsx' does not exist at src/components/toast/toast.tsx
|
||||
# check-registry-eject: 1 diagnostic(s) — see above.
|
||||
# (exit 1)
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
The exported helpers can be reused — for example, a CI report generator that walks both registries (`react-ui` + a future `react-ui-social`) and emits a unified report:
|
||||
|
||||
```ts
|
||||
import { audit } from "@suaveplan/react-ui/scripts/check-registry-eject";
|
||||
|
||||
const diagnostics = await audit({
|
||||
registryEntries: await listEntries(packageA),
|
||||
componentDirs: await listDirs(packageA),
|
||||
componentsDir: join(packageA, "src/components"),
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### CLI
|
||||
|
||||
`bun scripts/check-registry-eject.ts`
|
||||
|
||||
Exits 0 on a clean tree; exits 1 on any diagnostic; exits 2 when the registry directory or components directory is missing entirely (configuration error).
|
||||
|
||||
### Exported helpers
|
||||
|
||||
- `loadEntry(entryPath): Promise<RegistryEntry | { error }>` — reads + validates a single `registry/r/<name>.json` entry. Returns `{ error: string }` on any structural failure (missing field, wrong type, malformed JSON).
|
||||
- `audit({ registryEntries, componentDirs, componentsDir }): Promise<Diagnostic[]>` — pure function returning sorted diagnostics. The four diagnostic kinds are: `MISSING_REGISTRY_ENTRY`, `MISSING_SOURCE_FILE`, `EMPTY_FILES_LIST`, `MALFORMED_ENTRY`, `ORPHAN_REGISTRY_ENTRY`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The gate is intentionally narrower than the proposal's full vision — it verifies file presence + entry shape, NOT that the ejected source compiles against a synthesized consumer `tsconfig.json`. The synthesized-tsconfig roundtrip depends on the registry transform itself (rewriting `@suaveplan/react-ui` imports to consumer-local paths) which today is a passthrough — `build-registry.ts` ships source verbatim, so a consumer's `shadcn add` already gets compilable source. When the transform lands as a callable helper (per the comment block in `build-registry.ts`), this gate's `audit()` will be extended to invoke it and then run `tsc --noEmit` against the transformed tree.
|
||||
- `index.json` is excluded from the per-entry walk — it is the manifest of manifests, not an individual component.
|
||||
- Component directories whose only files are barrels (`index.ts` only, no `<name>.tsx`/`<name>.ts`) are NOT detected as component directories. The list-detection mirrors `build-registry.ts`'s heuristic.
|
||||
- Both `.tsx` and `.ts` extensions are valid for the file listed in `files[]`. Components like `<Toast>` that ship as JSX-free TypeScript ship as `.ts`; the registry entry for them lists `<name>.ts` and the gate accepts it.
|
||||
166
docs/ui/react/react-ui/src/components/accordion/accordion.md
Normal file
166
docs/ui/react/react-ui/src/components/accordion/accordion.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# Accordion
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - accordion.variants — `accordionVariants`, `accordio…
|
||||
- [accordion.variants](./accordion.variants.md) — `accordionVariants`, `accordionItemVariants`, `accordionHeaderVariants`, `accordionTriggerVariants`, and `accordionCont…
|
||||
- accordion.variants — `accordionVariants`, `accordio…
|
||||
|
||||
- [accordion.variants](./accordion.variants.md) — `accordionVariants`, `accordionItemVariants`, `accordionHeaderVariants`, `accordionTriggerVariants`, and `accordionCont…
|
||||
|
||||
- accordion.variants — `accordionVariants`, `accordio…
|
||||
|
||||
- [accordion.variants](./accordion.variants.md) — `accordionVariants`, `accordionItemVariants`, `accordionHeaderVariants`, `accordionTriggerVariants`, and `accordionCont…
|
||||
|
||||
- accordion.variants — `accordionVariants`, `accordio…
|
||||
|
||||
- [accordion.variants](./accordion.variants.md) — `accordionVariants`, `accordionItemVariants`, `accordionHeaderVariants`, `accordionTriggerVariants`, and `accordionCont…
|
||||
- [accordion.variants](./accordion.variants.md) — `accordionVariants`, `accordionItemVariants`, `accordionHeaderVariants`, `accordionTriggerVariants`, and `accordionCont…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Accordion>`, `<AccordionItem>`, `<AccordionHeader>`, `<AccordionTrigger>`, and `<AccordionContent>` are the chassis multi-section disclosure primitives built on `@radix-ui/react-accordion`. Radix supplies the WAI-ARIA accordion semantics: each trigger renders a `<button>` inside an `<h3>` header with `aria-expanded` reflecting the open state and `aria-controls` referencing its content panel; sibling triggers participate in a roving tabindex so ArrowUp/ArrowDown move focus between them and Home/End jump to first/last. Space and Enter activate the focused trigger. The chassis layers cva slots for every part and rotates a `<ChevronDownIcon>` inside the trigger when the active item opens.
|
||||
|
||||
Use `type="single"` for radio-style accordions where one item opens at a time (`collapsible` lets the active item be re-clicked closed); use `type="multiple"` for checkbox-style accordions where any number of items may be open simultaneously. The two modes are a TypeScript discriminated union — `value` is `string` under `single` and `string[]` under `multiple` — and the chassis preserves the discrimination so consumer call sites get accurate autocomplete.
|
||||
|
||||
## Features
|
||||
|
||||
- WAI-ARIA accordion semantics auto-managed by Radix (trigger `aria-expanded` / `aria-controls`, header `<h3>` wrapper, roving tabindex across triggers).
|
||||
- Keyboard navigation: ArrowUp / ArrowDown move between triggers, Home / End jump to first / last, Space / Enter activate the focused trigger.
|
||||
- `type="single"` / `type="multiple"` discriminated union with mode-appropriate `value` / `defaultValue` types.
|
||||
- Chevron icon (`<ChevronDownIcon>` from lucide) rendered inside the trigger; rotates 180deg on the open transition via the cva's `data-[state=open]>[data-icon='chevron']:rotate-180` selector.
|
||||
- Smooth open/close animation via `--radix-accordion-content-height`; collapses to none under `prefers-reduced-motion: reduce`.
|
||||
- Theme-driven cva overrides for `Accordion`, `AccordionItem`, `AccordionHeader`, `AccordionTrigger`, and `AccordionContent`.
|
||||
- All sub-components forward refs to their underlying Radix elements.
|
||||
- `disabled` at the Root level short-circuits all triggers; per-item `disabled` (Radix-native on `<AccordionItem>`) lets a single item be locked.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function Faq() {
|
||||
return (
|
||||
<Accordion type="single" collapsible>
|
||||
<AccordionItem value="shipping">
|
||||
<AccordionTrigger>How long does shipping take?</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
Standard delivery is 3–5 business days.
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem value="returns">
|
||||
<AccordionTrigger>What is the returns policy?</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
Items can be returned within 30 days for a full refund.
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@suaveplan/react-ui";
|
||||
import { useState } from "react";
|
||||
|
||||
interface Section {
|
||||
readonly id: string;
|
||||
readonly title: string;
|
||||
readonly body: React.ReactNode;
|
||||
}
|
||||
|
||||
interface MultiAccordionProps {
|
||||
readonly sections: ReadonlyArray<Section>;
|
||||
readonly defaultOpenIds?: ReadonlyArray<string>;
|
||||
readonly onOpenChange?(openIds: ReadonlyArray<string>): void;
|
||||
}
|
||||
|
||||
export function MultiAccordion({
|
||||
sections,
|
||||
defaultOpenIds,
|
||||
onOpenChange,
|
||||
}: MultiAccordionProps) {
|
||||
const [openIds, setOpenIds] = useState<string[]>([
|
||||
...(defaultOpenIds ?? []),
|
||||
]);
|
||||
|
||||
function handleChange(next: string[]): void {
|
||||
setOpenIds(next);
|
||||
onOpenChange?.(next);
|
||||
}
|
||||
|
||||
return (
|
||||
<Accordion
|
||||
type="multiple"
|
||||
value={openIds}
|
||||
onValueChange={handleChange}
|
||||
className="rounded-md border"
|
||||
>
|
||||
{sections.map((section) => (
|
||||
<AccordionItem key={section.id} value={section.id}>
|
||||
<AccordionTrigger>{section.title}</AccordionTrigger>
|
||||
<AccordionContent>{section.body}</AccordionContent>
|
||||
</AccordionItem>
|
||||
))}
|
||||
</Accordion>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Accordion>`
|
||||
|
||||
Renders the Radix Root that owns the open-set state machine.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `type` | `"single" \| "multiple"` | _required_ | Discriminator; controls the shape of `value` / `defaultValue` and whether siblings can be open at once. |
|
||||
| `value` | `string` (single) \| `string[]` (multiple) | _uncontrolled_ | Controlled open set. |
|
||||
| `defaultValue` | `string` (single) \| `string[]` (multiple) | _none_ | Initial open set for uncontrolled use. |
|
||||
| `onValueChange` | `(value: string) => void` (single) \| `(value: string[]) => void` (multiple) | _none_ | Fires on every open / close transition. |
|
||||
| `collapsible` | `boolean` | `false` | When `type="single"`, lets the active item be re-clicked closed. |
|
||||
| `disabled` | `boolean` | `false` | Disables every trigger. |
|
||||
| `dir` | `"ltr" \| "rtl"` | inherited | Reading direction; affects ArrowLeft / ArrowRight handling on horizontal accordions. |
|
||||
| `orientation` | `"horizontal" \| "vertical"` | `"vertical"` | Layout orientation; vertical (default) responds to ArrowUp / ArrowDown, horizontal to ArrowLeft / ArrowRight. |
|
||||
|
||||
### `<AccordionItem>`
|
||||
|
||||
Wraps the Radix Item; carries a unique `value` string used by the Root to discriminate the open set.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `value` | `string` | _required_ | Unique identifier within this Accordion. |
|
||||
| `disabled` | `boolean` | `false` | Disables the trigger inside this item. |
|
||||
|
||||
### `<AccordionTrigger>`
|
||||
|
||||
Wraps the Radix Trigger and renders the chevron icon. Always sits inside an `<AccordionItem>`.
|
||||
|
||||
### `<AccordionHeader>`
|
||||
|
||||
The `<h3>` wrapper around the trigger. `<AccordionTrigger>` already wraps itself in this header — render `<AccordionHeader>` directly only when you need a custom heading element.
|
||||
|
||||
### `<AccordionContent>`
|
||||
|
||||
Wraps the Radix Content. Children unmount when closed (Radix default). The chassis pads the content with `pb-3 pt-0` so the body sits below the trigger without an awkward gap above.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The Radix Root is a discriminated union on `type`; the chassis surface preserves the union via `AccordionProps = ComponentPropsWithoutRef<typeof AccordionPrimitive.Root>`. Internally we cast through `unknown` because TypeScript cannot prove the spread preserves the discrimination — the runtime accepts both shapes equivalently.
|
||||
- The chevron icon is rendered as a sibling of the trigger label so the `data-[state=open]>[data-icon='chevron']:rotate-180` cva selector can flip it without escaping the cva surface.
|
||||
- The chassis NEVER assigns a numeric `z-index` (per the shadcn skill rule). The accordion sits in normal flow.
|
||||
- The animation classes (`animate-collapsible-down/up`) are reused from the Collapsible cva so a single set of keyframes covers both primitives.
|
||||
- Per WAI-ARIA, the trigger is wrapped in an `<h3>` header so screen-readers announce the item as a heading. Consumers may render a custom `<AccordionHeader>` to change the heading level when nested inside a heading hierarchy.
|
||||
@@ -0,0 +1,83 @@
|
||||
# Accordion Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`accordionVariants`, `accordionItemVariants`, `accordionHeaderVariants`, `accordionTriggerVariants`, and `accordionContentVariants` are the cva (class-variance-authority) configurations powering every `<Accordion>` slot. They are split from `accordion.tsx` into their own module so an ejected source copy under `npx shadcn@latest add @suaveplan/accordion` keeps the cva configurations in a dedicated file. Theme overrides flow through `useThemedVariants("Accordion<Slot>")`.
|
||||
|
||||
The root cva stacks items vertically with no gap so per-item bottom borders form a continuous separator stroke. The trigger cva carries focus / hover styling and the chevron-rotation hook. The content cva matches the Collapsible animation recipe so a single set of keyframes covers both primitives.
|
||||
|
||||
## Features
|
||||
|
||||
- Root: `flex flex-col` so items stack vertically.
|
||||
- Item: `border-b last:border-b-0` so adjacent items appear connected; the final item drops the bottom stroke.
|
||||
- Header: `flex` with default heading margins stripped via the Radix render path.
|
||||
- Trigger: full-width flex row with end-aligned chevron, `hover:underline`, `focus-visible:ring-2`, `disabled:opacity-50`. The `[&[data-state=open]>[data-icon='chevron']]:rotate-180` selector flips the chevron on the open transition; `[&>[data-icon='chevron']]:transition-transform` smooths the rotation.
|
||||
- Content: `overflow-hidden text-sm` plus `data-[state=open]:animate-collapsible-down` / `data-[state=closed]:animate-collapsible-up` so Radix's `--radix-accordion-content-height` CSS variable animates cleanly. `motion-reduce:animate-none` collapses the transition under `prefers-reduced-motion: reduce`.
|
||||
- NEVER carries a numeric `z-index` (the accordion sits in normal flow).
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
accordionContentVariants,
|
||||
accordionTriggerVariants,
|
||||
accordionVariants,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
const rootClassName = accordionVariants();
|
||||
const triggerClassName = accordionTriggerVariants();
|
||||
const contentClassName = accordionContentVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { accordionTriggerVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomTrigger() {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
aria-expanded
|
||||
data-state="closed"
|
||||
className={cn(
|
||||
accordionTriggerVariants(),
|
||||
"rounded bg-muted px-3"
|
||||
)}
|
||||
>
|
||||
Custom row
|
||||
<span data-icon="chevron">⌄</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `accordionVariants(): string`
|
||||
|
||||
Returns the root grouping classes; takes no arguments.
|
||||
|
||||
### `accordionItemVariants(): string`
|
||||
|
||||
Returns the per-item border classes; takes no arguments.
|
||||
|
||||
### `accordionHeaderVariants(): string`
|
||||
|
||||
Returns the `<h3>` wrapper classes; takes no arguments.
|
||||
|
||||
### `accordionTriggerVariants(): string`
|
||||
|
||||
Returns the trigger button classes (including the chevron rotation selector); takes no arguments.
|
||||
|
||||
### `accordionContentVariants(): string`
|
||||
|
||||
Returns the content panel classes; takes no arguments.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chevron rotation is implemented via a Tailwind arbitrary-variant selector (`[&[data-state=open]>[data-icon='chevron']]:rotate-180`) so the trigger does not need to render its own state-aware element. Any sibling carrying `data-icon="chevron"` will rotate.
|
||||
- The trigger's `motion-reduce` rule covers BOTH the trigger transition AND the chevron rotation (`motion-reduce:[&>[data-icon='chevron']]:transition-none`) so reduced-motion users see no animation at all.
|
||||
- The content cva is intentionally identical to `collapsibleContentVariants` so an editor switching between Accordion and Collapsible sees the same animation behaviour.
|
||||
- Per the shadcn skill rule, the cva NEVER assigns a numeric `z-index`. The accordion renders inline in the document flow.
|
||||
- The `last:border-b-0` rule prevents a double-border problem when the accordion sits inside a bordered container.
|
||||
@@ -0,0 +1,163 @@
|
||||
# AlertDialog
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - alert-dialog.variants — `alertDialogOverlayVariants…
|
||||
- [alert-dialog.variants](./alert-dialog.variants.md) — `alertDialogOverlayVariants`, `alertDialogContentVariants`, `alertDialogHeaderVariants`, `alertDialogFooterVariants`, `…
|
||||
- alert-dialog.variants — `alertDialogOverlayVariants…
|
||||
|
||||
- [alert-dialog.variants](./alert-dialog.variants.md) — `alertDialogOverlayVariants`, `alertDialogContentVariants`, `alertDialogHeaderVariants`, `alertDialogFooterVariants`, `…
|
||||
|
||||
- alert-dialog.variants — `alertDialogOverlayVariants…
|
||||
|
||||
- [alert-dialog.variants](./alert-dialog.variants.md) — `alertDialogOverlayVariants`, `alertDialogContentVariants`, `alertDialogHeaderVariants`, `alertDialogFooterVariants`, `…
|
||||
|
||||
- alert-dialog.variants — `alertDialogOverlayVariants…
|
||||
|
||||
- [alert-dialog.variants](./alert-dialog.variants.md) — `alertDialogOverlayVariants`, `alertDialogContentVariants`, `alertDialogHeaderVariants`, `alertDialogFooterVariants`, `…
|
||||
- [alert-dialog.variants](./alert-dialog.variants.md) — `alertDialogOverlayVariants`, `alertDialogContentVariants`, `alertDialogHeaderVariants`, `alertDialogFooterVariants`, `…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<AlertDialog>` and its sub-components (`<AlertDialogTrigger>`, `<AlertDialogPortal>`, `<AlertDialogOverlay>`, `<AlertDialogContent>`, `<AlertDialogHeader>`, `<AlertDialogFooter>`, `<AlertDialogTitle>`, `<AlertDialogDescription>`, `<AlertDialogAction>`, `<AlertDialogCancel>`) are the chassis modal CONFIRMATION dialog primitives built on `@radix-ui/react-alert-dialog`.
|
||||
|
||||
The structural difference between `<AlertDialog>` and `<Dialog>` is the click-outside-suppression: an alert dialog CANNOT be dismissed by clicking the overlay backdrop — the user MUST press the `<AlertDialogAction>` or `<AlertDialogCancel>` button. Use `<AlertDialog>` for destructive operations (delete / discard / log out) where an accidental backdrop click could lose work; use `<Dialog>` for everything else.
|
||||
|
||||
Per the WAI-ARIA alertdialog pattern, every alert dialog MUST contain an `<AlertDialogTitle>` so the accessible name is announced. Use `className="sr-only"` if the visible UI does not need a heading. Radix renders `role="alertdialog"` (NOT `role="dialog"`) so screen readers know to announce the prompt as a high-priority interrupt.
|
||||
|
||||
## Features
|
||||
|
||||
- `role="alertdialog"` (Radix-managed) so screen readers announce a high-priority prompt.
|
||||
- Click-outside dismissal SUPPRESSED — only Action / Cancel resolve the dialog.
|
||||
- Escape closes (treated as cancel) — Radix-managed.
|
||||
- Focus-trap on open; restore-focus to the trigger on close.
|
||||
- Four content sizes (`sm`, `default`, `lg`, `xl`) scale the panel max-width.
|
||||
- `<AlertDialogAction>` defaults to Button `variant="default"` styling (primary).
|
||||
- `<AlertDialogCancel>` defaults to Button `variant="outline"` styling (secondary).
|
||||
- Theme-driven cva overrides for every slot via `useThemedVariants("AlertDialog<Slot>")`.
|
||||
- All sub-components forward refs.
|
||||
- `motion-reduce:animate-none` collapses animations under `prefers-reduced-motion: reduce`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function ConfirmDelete() {
|
||||
return (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger>Delete account</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
This action cannot be undone.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction>Delete</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from "@suaveplan/react-ui";
|
||||
import { useState } from "react";
|
||||
|
||||
export function ConfirmDestructive({
|
||||
title,
|
||||
description,
|
||||
onConfirm,
|
||||
}: {
|
||||
readonly title: string;
|
||||
readonly description: string;
|
||||
readonly onConfirm: () => Promise<void>;
|
||||
}) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
async function handleConfirm() {
|
||||
setBusy(true);
|
||||
try {
|
||||
await onConfirm();
|
||||
setOpen(false);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<AlertDialog open={open} onOpenChange={setOpen}>
|
||||
<AlertDialogContent size="sm">
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>{title}</AlertDialogTitle>
|
||||
<AlertDialogDescription>{description}</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel disabled={busy}>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={handleConfirm} disabled={busy}>
|
||||
{busy ? "Working…" : "Confirm"}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<AlertDialog>`
|
||||
|
||||
Wraps the Radix AlertDialog Root. Same lifecycle props as `<Dialog>` (`open`, `defaultOpen`, `onOpenChange`).
|
||||
|
||||
### `<AlertDialogContent>`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `size` | `"sm" \| "default" \| "lg" \| "xl"` | `"default"` | Visual variant scaling the panel max-width. |
|
||||
|
||||
### `<AlertDialogTitle>`
|
||||
|
||||
REQUIRED for accessibility. Render `className="sr-only"` if no visible heading is needed.
|
||||
|
||||
### `<AlertDialogAction>`, `<AlertDialogCancel>`
|
||||
|
||||
Renders as buttons with the default Button `variant="default"` and `variant="outline"` styling respectively. Activating either resolves the dialog (closes it).
|
||||
|
||||
### `<AlertDialogTrigger>`, `<AlertDialogPortal>`, `<AlertDialogOverlay>`, `<AlertDialogHeader>`, `<AlertDialogFooter>`, `<AlertDialogDescription>`
|
||||
|
||||
Direct re-exports of the Radix sub-components with the chassis cva applied.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis NEVER assigns a numeric `z-index` — Radix portals handle stacking.
|
||||
- The Action / Cancel button cva is INLINED into AlertDialog rather than reusing `<Button>` so an ejected source copy stays self-contained (matches the shadcn upstream `alert-dialog` recipe).
|
||||
- `<AlertDialogContent>` self-portals via `<AlertDialogPortal>` so consumers do not need to compose the portal manually.
|
||||
- The Cancel button uses `mt-2 sm:mt-0` so it stacks beneath the Action on small screens (Action becomes the visually emphasised primary-end position) and aligns horizontally above `sm`.
|
||||
- Radix's a11y warning fires when no `<AlertDialogDescription>` is provided either — the chassis does not enforce this since some single-line confirmations only need a title; consumers can pass `aria-describedby={undefined}` on `<AlertDialogContent>` if they want to suppress the warning explicitly.
|
||||
@@ -0,0 +1,69 @@
|
||||
# AlertDialog Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`alertDialogOverlayVariants`, `alertDialogContentVariants`, `alertDialogHeaderVariants`, `alertDialogFooterVariants`, `alertDialogTitleVariants`, `alertDialogDescriptionVariants`, `alertDialogActionVariants`, and `alertDialogCancelVariants` are the cva configurations powering every `<AlertDialog>` slot. They are split from `alert-dialog.tsx` into their own module so an ejected source copy under `npx shadcn@latest add @suaveplan/alert-dialog` keeps the cva configurations in a dedicated file. Theme overrides flow through `useThemedVariants("AlertDialog<Slot>")`.
|
||||
|
||||
The Action and Cancel button cva configurations duplicate the Button `variant="default"` / `variant="outline"` recipes so the alert dialog stays self-contained — an ejected copy does not require pulling in `<Button>` separately.
|
||||
|
||||
## Features
|
||||
|
||||
- Overlay: `fixed inset-0 bg-black/80` matching Dialog/Sheet visual language.
|
||||
- Content: same centred panel + four sizes (`sm` / `default` / `lg` / `xl`) as Dialog, allowing consumers to scale the confirmation surface for short vs verbose copy.
|
||||
- Header / footer: matched to Dialog so visual cadence stays identical.
|
||||
- Title: `text-lg font-semibold` (no `tracking-tight` so confirmation prompts sit slightly looser than narrative dialogs).
|
||||
- Description: muted-foreground text-sm.
|
||||
- Action button: `bg-primary text-primary-foreground hover:bg-primary/90` (Button `variant="default"` recipe).
|
||||
- Cancel button: `border border-input bg-background hover:bg-accent` (Button `variant="outline"` recipe), with `mt-2 sm:mt-0` for small-screen stacking.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
alertDialogContentVariants,
|
||||
alertDialogActionVariants,
|
||||
alertDialogCancelVariants,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
const contentClassName = alertDialogContentVariants({ size: "sm" });
|
||||
const actionClassName = alertDialogActionVariants();
|
||||
const cancelClassName = alertDialogCancelVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { alertDialogContentVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomAlert() {
|
||||
return (
|
||||
<div
|
||||
role="alertdialog"
|
||||
aria-modal="true"
|
||||
className={cn(
|
||||
alertDialogContentVariants({ size: "sm" }),
|
||||
"border-2 border-destructive"
|
||||
)}
|
||||
>
|
||||
…
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `alertDialogOverlayVariants(): string`
|
||||
- `alertDialogContentVariants(props?: { size?: "sm" | "default" | "lg" | "xl" }): string`
|
||||
- `alertDialogHeaderVariants(): string`
|
||||
- `alertDialogFooterVariants(): string`
|
||||
- `alertDialogTitleVariants(): string`
|
||||
- `alertDialogDescriptionVariants(): string`
|
||||
- `alertDialogActionVariants(): string`
|
||||
- `alertDialogCancelVariants(): string`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The Action / Cancel cva duplicate the Button cva recipes intentionally — keeping the alert dialog self-contained means ejecting the alert dialog source via `npx shadcn add` does not implicitly require ejecting `<Button>` as well. Consumers who want a unified visual language can override the Action cva via theme `variantOverrides.AlertDialogAction`.
|
||||
- Per the shadcn skill rule, the cva NEVER assigns a numeric `z-index`. Radix's `<Portal>` mounts the content above all page content via DOM placement.
|
||||
- The Cancel `mt-2 sm:mt-0` ensures Cancel stacks beneath the Action on mobile (Action becomes visually prominent, aligning with the accept-as-default action) and horizontally aligns above `sm`.
|
||||
125
docs/ui/react/react-ui/src/components/alert/alert.md
Normal file
125
docs/ui/react/react-ui/src/components/alert/alert.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# Alert
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - alert.variants — `alertVariants` is the cva config……
|
||||
- [alert.variants](./alert.variants.md) — `alertVariants` is the cva config that powers the `<Alert>` chassis component. It exposes a `variant` axis with four ru…
|
||||
- alert.variants — `alertVariants` is the cva config……
|
||||
|
||||
- [alert.variants](./alert.variants.md) — `alertVariants` is the cva config that powers the `<Alert>` chassis component. It exposes a `variant` axis with four ru…
|
||||
|
||||
- alert.variants — `alertVariants` is the cva config……
|
||||
|
||||
- [alert.variants](./alert.variants.md) — `alertVariants` is the cva config that powers the `<Alert>` chassis component. It exposes a `variant` axis with four ru…
|
||||
|
||||
- alert.variants — `alertVariants` is the cva config…
|
||||
|
||||
- [alert.variants](./alert.variants.md) — `alertVariants` is the cva config that powers the `<Alert>` chassis component. It exposes a `variant` axis with four ru…
|
||||
- [alert.variants](./alert.variants.md) — `alertVariants` is the cva config that powers the `<Alert>` chassis component. It exposes a `variant` axis with four ru…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Alert>` is the chassis callout for inline messages, errors, and notices. It ships as a family of four components — `<Alert>` (root), `<AlertTitle>`, `<AlertDescription>`, `<AlertIcon>` — that compose into the conventional shadcn alert layout. Per the shadcn skill rule, ALWAYS use `<Alert>` for callouts rather than building custom styled divs (`<div className="border-l-4 border-yellow-500 ...">`) — hand-rolled callouts diverge across teams in colour scale, iconography, accessibility hooks, and dark-mode treatment.
|
||||
|
||||
The chassis renders `role="alert"` on the root so screen-readers announce the message. The `variant` axis swaps semantic colour pairs for four rungs (`default` / `destructive` / `success` / `warning`), and `<AlertIcon>` selects the matching lucide-react icon (`InfoIcon` / `AlertCircleIcon` / `CheckCircle2Icon` / `AlertTriangleIcon`) for the variant.
|
||||
|
||||
## Features
|
||||
|
||||
- Four colour variants via the `variant` axis (`default` / `destructive` / `success` / `warning`).
|
||||
- Four composition primitives — `Alert`, `AlertTitle`, `AlertDescription`, `AlertIcon`.
|
||||
- `role="alert"` automatically applied so screen-readers announce the message.
|
||||
- `data-variant` attribute on the rendered root so tests / consumer CSS can target alerts by variant.
|
||||
- `[&>svg]:` Tailwind arbitrary modifiers position any direct svg child as an inline icon — no consumer flex setup required.
|
||||
- `<AlertTitle>` renders as `<h5>` so it nests under page-level headings without disrupting the document outline.
|
||||
- `<AlertDescription>` renders as `<div>` so consumers can compose paragraphs / lists / inline elements without nesting block elements inside a `<p>`.
|
||||
- All sub-components forward refs to their underlying DOM nodes.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Alert,
|
||||
AlertDescription,
|
||||
AlertIcon,
|
||||
AlertTitle,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function PolicyChange() {
|
||||
return (
|
||||
<Alert variant="warning">
|
||||
<AlertIcon variant="warning" />
|
||||
<AlertTitle>Policy update</AlertTitle>
|
||||
<AlertDescription>
|
||||
Your team's retention policy has been updated.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Alert,
|
||||
AlertDescription,
|
||||
AlertTitle,
|
||||
Button,
|
||||
} from "@suaveplan/react-ui";
|
||||
import { ZapIcon } from "lucide-react";
|
||||
|
||||
export function CustomIconAlert() {
|
||||
return (
|
||||
<Alert variant="success">
|
||||
<ZapIcon className="size-4" aria-hidden="true" />
|
||||
<AlertTitle>Sync complete</AlertTitle>
|
||||
<AlertDescription>
|
||||
<p>523 records synchronised in 1.4 seconds.</p>
|
||||
<Button variant="outline" size="sm" className="mt-2">
|
||||
View report
|
||||
</Button>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Alert>`
|
||||
|
||||
`forwardRef<HTMLDivElement, AlertProps>` — root container. Renders `role="alert"` and `data-variant`.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `variant` | `"default" \| "destructive" \| "success" \| "warning"` | `"default"` | Colour palette. |
|
||||
| _all_ `HTMLAttributes<HTMLDivElement>` | _native_ | _native_ | Forwarded as-is. |
|
||||
|
||||
### `<AlertTitle>`
|
||||
|
||||
`forwardRef<HTMLHeadingElement, AlertTitleProps>` — heading rendered as `<h5>`.
|
||||
|
||||
### `<AlertDescription>`
|
||||
|
||||
`forwardRef<HTMLDivElement, AlertDescriptionProps>` — body text. Rendered as `<div>` to allow nested block elements.
|
||||
|
||||
### `<AlertIcon>`
|
||||
|
||||
`function (props: AlertIconProps): ReactElement` — convenience wrapper that renders the lucide-react icon for the supplied `variant`. Marked `aria-hidden="true"` so the visible title / description carries the accessible name. Not a `forwardRef` (icons are stateless and the parent `<Alert>` reaches them via the cva `[&>svg]:` modifiers, not via ref-based DOM access).
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `variant` | same as `<Alert>` | `"default"` | Variant whose default icon to render. |
|
||||
| `className` | `string` | _undefined_ | Optional className passed to the rendered icon. |
|
||||
|
||||
#### Errors
|
||||
|
||||
The Alert components do NOT call `useTranslation()` so they do not interact with the i18n provider. Children carrying user-perceivable text must be resolved at the call site with `t('react-ui.alert.policy.title')` or similar.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `role="alert"` is unconditionally applied. This may produce verbose announcements when many alerts mount at once (e.g. a list of validation errors); consumers in that scenario should wrap the whole list in `<div role="alert" aria-live="polite">` and pass `role="presentation"` to each `<Alert>` to suppress duplicate announcements (or use `<Alert>` purely for visual styling and let the parent live region carry the accessibility semantics).
|
||||
- The `[&>svg]:absolute [&>svg]:start-4 [&>svg]:top-4` rules in the cva position any direct SVG child as the inline icon. This means consumers can drop any icon — `<MailIcon />`, `<SparklesIcon />`, `<AlertIcon variant="warning" />` — directly inside the alert root and it lands correctly without a wrapper element.
|
||||
- `<AlertIcon>` is a convenience wrapper, not a required slot. Alerts without any icon (just title + description) render correctly — the magic-modifier rules only fire when a direct SVG child exists.
|
||||
- `<AlertTitle>` defaults to `<h5>` because alerts typically appear inside a section that already has its own `<h2>` / `<h3>` heading. Consumers needing a different heading level should compose their own heading element with `alertTitleVariants()`.
|
||||
- `<AlertDescription>` renders as `<div>` (not `<p>`) so consumers can compose nested block elements (paragraphs, lists, buttons) without invalid HTML — paragraphs cannot contain block elements.
|
||||
@@ -0,0 +1,57 @@
|
||||
# Alert Variants
|
||||
|
||||
## Purpose
|
||||
|
||||
`alertVariants` is the cva config that powers the `<Alert>` chassis component. It exposes a `variant` axis with four rungs (`default` / `destructive` / `success` / `warning`) that each pair a semantic border colour with matching foreground and icon tints. The base class string includes the magic `[&>svg]:` modifiers that position any direct SVG child as an inline icon in the top-start corner without requiring consumer flex setup — this is the same trick the upstream shadcn `alert` recipe uses.
|
||||
|
||||
`alertTitleVariants` and `alertDescriptionVariants` are presentation-only with no exposed axes — they encode the conventional spacing and typography for the title / body rows.
|
||||
|
||||
## Features
|
||||
|
||||
- Four colour variants via the `variant` axis (`default` / `destructive` / `success` / `warning`).
|
||||
- `[&>svg]:absolute [&>svg]:start-4 [&>svg]:top-4` magic modifiers so any direct svg child positions correctly without a wrapper.
|
||||
- `[&>svg~*]:ps-7` ensures siblings of the svg get logical-property left padding so RTL layouts work without bespoke markup.
|
||||
- `[&>svg+div]:translate-y-[-3px]` nudges the title row up by 3px so its baseline aligns with the icon centre.
|
||||
- Title is `font-medium leading-none tracking-tight`; description is `text-sm` with `leading-relaxed` on nested `<p>` elements.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { alertVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const default_ = alertVariants();
|
||||
const danger = alertVariants({ variant: "destructive" });
|
||||
const success = alertVariants({ variant: "success" });
|
||||
const warning = alertVariants({ variant: "warning" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { cva } from "class-variance-authority";
|
||||
import { alertVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const stickyAlert = cva(
|
||||
[alertVariants({ variant: "warning" }), "sticky top-0 z-30"].join(" ")
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `alertVariants(opts?)`
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `variant` | `"default" \| "destructive" \| "success" \| "warning"` | `"default"` | Colour palette for border, foreground, and inline icon. |
|
||||
|
||||
### `alertTitleVariants()`, `alertDescriptionVariants()`
|
||||
|
||||
No options — single canonical class strings.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The `[&>svg]:` Tailwind arbitrary descendant modifiers are how shadcn positions the icon — any direct SVG child of the alert is auto-placed in the top-start corner (`absolute start-4 top-4`). This avoids the need for a separate `<AlertIcon>` slot at the JSX level — consumers can drop any icon component directly inside the alert root.
|
||||
- The chassis ships an `<AlertIcon>` convenience wrapper that picks the lucide-react default for the alert variant, but it is optional — consumers can supply any icon (`<MailIcon />`, `<SparklesIcon />`, etc.) inline.
|
||||
- `[&>svg~*]:ps-7` uses logical-property `ps-7` (padding-inline-start) so siblings of the icon space correctly in both LTR and RTL layouts.
|
||||
- The destructive variant keeps the same `border-destructive` colour in dark mode (no `dark:` override) because the destructive token is already calibrated for both colour schemes via the theme `cssVariables`.
|
||||
- Success / warning variants use Tailwind's `emerald-*` and `amber-*` palettes rather than relying on theme tokens because they are semantic-fixed colours; consumers wanting to swap them can register a `variantOverrides.Alert` on their theme plugin.
|
||||
@@ -0,0 +1,124 @@
|
||||
# AspectRatio
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - aspect-ratio.variants — `aspectRatioVariants` is th…
|
||||
- [aspect-ratio.variants](./aspect-ratio.variants.md) — `aspectRatioVariants` is the cva (class-variance-authority) configuration powering the `<AspectRatio>` outer wrapper. I…
|
||||
- aspect-ratio.variants — `aspectRatioVariants` is th…
|
||||
|
||||
- [aspect-ratio.variants](./aspect-ratio.variants.md) — `aspectRatioVariants` is the cva (class-variance-authority) configuration powering the `<AspectRatio>` outer wrapper. I…
|
||||
|
||||
- aspect-ratio.variants — `aspectRatioVariants` is th…
|
||||
|
||||
- [aspect-ratio.variants](./aspect-ratio.variants.md) — `aspectRatioVariants` is the cva (class-variance-authority) configuration powering the `<AspectRatio>` outer wrapper. I…
|
||||
|
||||
- aspect-ratio.variants — `aspectRatioVariants` is th…
|
||||
|
||||
- [aspect-ratio.variants](./aspect-ratio.variants.md) — `aspectRatioVariants` is the cva (class-variance-authority) configuration powering the `<AspectRatio>` outer wrapper. I…
|
||||
- [aspect-ratio.variants](./aspect-ratio.variants.md) — `aspectRatioVariants` is the cva (class-variance-authority) configuration powering the `<AspectRatio>` outer wrapper. I…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<AspectRatio>` is the chassis fixed-aspect-ratio container built on `@radix-ui/react-aspect-ratio`. Radix sizes the inner box via inline `padding-bottom` so the rendered element preserves the requested numeric ratio at every viewport width — the wrapping `<div>` then serves as a positioning context for any embedded `<img>`, `<video>`, `<iframe>`, or other layout-sensitive child. The chassis layers a small cva recipe (`relative w-full overflow-hidden`) so embedded media slot in cleanly without consumers having to remember the boilerplate every time.
|
||||
|
||||
The component is purely structural — it takes the requested `ratio` prop, sizes the box, and renders children. No translations, no theming axes, no event handlers. The Radix primitive does not assign a numeric `z-index` and neither does the chassis, so stacking remains implicit.
|
||||
|
||||
## Features
|
||||
|
||||
- Wraps `@radix-ui/react-aspect-ratio` with a chassis-friendly cva slot.
|
||||
- `ratio` prop accepts any positive number (`16 / 9`, `4 / 3`, `1`, `21 / 9`, …); Radix interprets it as `width / height`.
|
||||
- `relative w-full overflow-hidden` baked in so embedded `<img className="h-full w-full object-cover">` or `<iframe className="absolute inset-0 size-full">` children sit correctly.
|
||||
- Forwards refs to the rendered `<div>`.
|
||||
- Theme-driven cva override via `useThemedVariants("AspectRatio")`.
|
||||
- No translations and no a11y semantics required (the box is layout-only).
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { AspectRatio } from "@suaveplan/react-ui";
|
||||
|
||||
export function CoverImage({ src, alt }: { src: string; alt: string }) {
|
||||
return (
|
||||
<AspectRatio ratio={16 / 9}>
|
||||
<img
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
</AspectRatio>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { AspectRatio, cn } from "@suaveplan/react-ui";
|
||||
|
||||
interface MediaTileProps {
|
||||
readonly kind: "image" | "video" | "embed";
|
||||
readonly src: string;
|
||||
readonly alt?: string;
|
||||
readonly title?: string;
|
||||
}
|
||||
|
||||
export function MediaTile({ kind, src, alt, title }: MediaTileProps) {
|
||||
return (
|
||||
<AspectRatio
|
||||
ratio={kind === "video" ? 16 / 9 : kind === "embed" ? 4 / 3 : 1}
|
||||
className={cn(
|
||||
"rounded-lg border bg-muted",
|
||||
kind === "embed" && "shadow-md"
|
||||
)}
|
||||
>
|
||||
{kind === "image" ? (
|
||||
<img
|
||||
src={src}
|
||||
alt={alt ?? ""}
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
) : kind === "video" ? (
|
||||
<video
|
||||
src={src}
|
||||
controls
|
||||
className="h-full w-full object-cover"
|
||||
aria-label={title}
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={src}
|
||||
title={title ?? "Embed"}
|
||||
className="absolute inset-0 size-full border-0"
|
||||
/>
|
||||
)}
|
||||
</AspectRatio>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<AspectRatio>`
|
||||
|
||||
Renders the Radix Root which constrains the box to the supplied numeric ratio.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `ratio` | `number` | `1` | Desired `width / height` ratio. Radix uses it to compute the `padding-bottom` percentage. |
|
||||
| `className` | `string` | _none_ | Additional classes merged via `cn()` after the chassis cva recipe. |
|
||||
| `children` | `ReactNode` | _none_ | The content to be sized to the ratio. Position children with `className="h-full w-full object-cover"` or `className="absolute inset-0 size-full"` for full-bleed behaviour. |
|
||||
|
||||
Forwards refs to the rendered `<div>` element.
|
||||
|
||||
### `aspectRatioVariants(): string`
|
||||
|
||||
Returns the chassis cva class string. Takes no arguments. Useful when assembling a custom slot that wants the same recipe without rendering an `<AspectRatio>`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The cva carries `relative` so embedded children using `absolute inset-0` (e.g. iframes) anchor correctly. Without it, the absolutely-positioned child escapes to the nearest positioned ancestor.
|
||||
- `overflow-hidden` is included so rounded corners on `className="rounded-lg"` clip the embedded media — without it, `<img>` overflow would leak out the corners.
|
||||
- `w-full` ensures the wrapper fills the parent's width; the height is then computed by Radix from the ratio. Consumers can override with `className="w-1/2"` or similar when a fixed-width tile is desired.
|
||||
- The chassis NEVER assigns a numeric `z-index`; stacking is the consumer's responsibility (typically managed by a surrounding overlay primitive such as `<Dialog>`).
|
||||
- Radix's primitive accepts `ratio` as a number; passing `0` or a negative value results in invalid CSS. The chassis does not validate at runtime — supply a positive ratio.
|
||||
- Because the component is purely structural, there are no translation keys to override and no a11y semantics injected. Any media inside should carry its own `alt` / `title` / `aria-label` per WAI-ARIA practice.
|
||||
@@ -0,0 +1,55 @@
|
||||
# AspectRatio Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`aspectRatioVariants` is the cva (class-variance-authority) configuration powering the `<AspectRatio>` outer wrapper. It is split from `aspect-ratio.tsx` into its own module so an ejected source copy under `npx shadcn@latest add @suaveplan/aspect-ratio` keeps the cva configuration in a dedicated file. Theme overrides flow through `useThemedVariants("AspectRatio")`.
|
||||
|
||||
The cva is intentionally minimal — no axes, no parameters. Radix already constrains the inner box via inline `padding-bottom`, so the only chassis decision is the structural wrapper recipe (`relative w-full overflow-hidden`).
|
||||
|
||||
## Features
|
||||
|
||||
- Single class string: `relative w-full overflow-hidden`.
|
||||
- `relative` so embedded `absolute inset-0` children (iframes) anchor correctly.
|
||||
- `w-full` so the wrapper fills the parent width by default; consumers can override.
|
||||
- `overflow-hidden` so rounded corners on the wrapper clip embedded media cleanly.
|
||||
- NEVER carries a numeric `z-index` (stacking is managed by the surrounding overlay primitive).
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { aspectRatioVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const className = aspectRatioVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { aspectRatioVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomMediaTile() {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
aspectRatioVariants(),
|
||||
"rounded-2xl border-2 border-primary"
|
||||
)}
|
||||
style={{ aspectRatio: "16 / 9" }}
|
||||
>
|
||||
…
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `aspectRatioVariants(): string`
|
||||
|
||||
Returns the chassis wrapper class string. No arguments — the variant config has no axes.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The cva exists primarily so a `ThemePlugin` may declare an override via `variantOverrides.AspectRatio` to swap the recipe (e.g. add `rounded-md` by default in a "soft" theme variant). When no theme override is registered, the base recipe is returned verbatim.
|
||||
- Per the shadcn skill rule, the cva NEVER assigns a numeric `z-index`. Radix's primitive does not portal the box, so stacking remains under the consumer's surrounding layout.
|
||||
- The cva does not include `block` because Radix's primitive renders an inline-styled `<div>` that already lays out as a block element.
|
||||
111
docs/ui/react/react-ui/src/components/avatar/avatar.md
Normal file
111
docs/ui/react/react-ui/src/components/avatar/avatar.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# Avatar
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - avatar.variants — `avatarVariants` is the cva confi…
|
||||
- [avatar.variants](./avatar.variants.md) — `avatarVariants` is the cva config that powers the `<Avatar>` chassis component (`<AvatarImage>` and `<AvatarFallback>`…
|
||||
- avatar.variants — `avatarVariants` is the cva confi…
|
||||
|
||||
- [avatar.variants](./avatar.variants.md) — `avatarVariants` is the cva config that powers the `<Avatar>` chassis component (`<AvatarImage>` and `<AvatarFallback>`…
|
||||
|
||||
- avatar.variants — `avatarVariants` is the cva confi…
|
||||
|
||||
- [avatar.variants](./avatar.variants.md) — `avatarVariants` is the cva config that powers the `<Avatar>` chassis component (`<AvatarImage>` and `<AvatarFallback>`…
|
||||
|
||||
- avatar.variants — `avatarVariants` is the cva confi…
|
||||
|
||||
- [avatar.variants](./avatar.variants.md) — `avatarVariants` is the cva config that powers the `<Avatar>` chassis component (`<AvatarImage>` and `<AvatarFallback>`…
|
||||
- [avatar.variants](./avatar.variants.md) — `avatarVariants` is the cva config that powers the `<Avatar>` chassis component (`<AvatarImage>` and `<AvatarFallback>`…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Avatar>` is the chassis circular image holder built on `@radix-ui/react-avatar`. It ships as a family of three components — `<Avatar>` (root), `<AvatarImage>`, `<AvatarFallback>` — that compose into the conventional shadcn avatar pattern. Per the shadcn skill rule, ALWAYS pair an `<AvatarImage>` with an `<AvatarFallback>` — Radix swaps the fallback in when the image source fails to load OR when no source is supplied. An Avatar without a fallback flashes empty while loading, then disappears silently on a 404 — both are bugs.
|
||||
|
||||
The root `<Avatar>` exposes a `size` axis with four rungs (`sm` / `default` / `lg` / `xl`) using Tailwind's `size-*` shorthand so width and height stay in lockstep, preventing off-by-one ratio bugs that arise when consumers set width and height independently.
|
||||
|
||||
## Features
|
||||
|
||||
- Four sizes via the `size` axis (`sm` / `default` / `lg` / `xl`) using Tailwind `size-*`.
|
||||
- Three composition primitives — `Avatar` (root), `AvatarImage`, `AvatarFallback`.
|
||||
- Radix-managed image-loading state machine — `<AvatarFallback>` swaps in automatically when the image source fails or has not yet loaded.
|
||||
- `aspect-square object-cover` on `<AvatarImage>` so portrait / landscape sources crop centrally rather than distorting.
|
||||
- Forwards refs to the underlying Radix elements on every sub-component.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@suaveplan/react-ui";
|
||||
|
||||
export function UserAvatar() {
|
||||
return (
|
||||
<Avatar>
|
||||
<AvatarImage src="/avatar.png" alt="Jane Doe" />
|
||||
<AvatarFallback>JD</AvatarFallback>
|
||||
</Avatar>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@suaveplan/react-ui";
|
||||
|
||||
interface AvatarStackUser {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly avatarUrl?: string;
|
||||
}
|
||||
|
||||
export function AvatarStack({ users }: { users: AvatarStackUser[] }) {
|
||||
return (
|
||||
<div className="flex -space-x-2">
|
||||
{users.slice(0, 3).map((user) => (
|
||||
<Avatar key={user.id} size="lg" className="border-2 border-background">
|
||||
{user.avatarUrl != null ? (
|
||||
<AvatarImage src={user.avatarUrl} alt={user.name} />
|
||||
) : null}
|
||||
<AvatarFallback>
|
||||
{user.name
|
||||
.split(" ")
|
||||
.slice(0, 2)
|
||||
.map((part) => part[0])
|
||||
.join("")}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Avatar>`
|
||||
|
||||
`forwardRef<…, AvatarProps>` — root container backed by `@radix-ui/react-avatar` `Root`.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `size` | `"sm" \| "default" \| "lg" \| "xl"` | `"default"` | Diameter — uses Tailwind `size-*`. |
|
||||
| _Radix props_ | _Radix-typed_ | — | All `AvatarPrimitive.Root` props. |
|
||||
|
||||
### `<AvatarImage>`
|
||||
|
||||
`forwardRef<…, AvatarImageProps>` — image source. Cover-fits inside the circular surface. Radix swaps in `<AvatarFallback>` automatically when the source fails to load.
|
||||
|
||||
### `<AvatarFallback>`
|
||||
|
||||
`forwardRef<…, AvatarFallbackProps>` — fallback surface. Per shadcn skill rule, REQUIRED in every Avatar. Typical contents are user initials (one or two letters) or an icon.
|
||||
|
||||
#### Errors
|
||||
|
||||
The Avatar components do NOT call `useTranslation()` so they do not interact with the i18n provider. The image's `alt` attribute and the fallback content are consumer-provided; consumers wanting translated alt text should pass `t('user.avatar.alt')` at the call site.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `size-*` is used at the root so width and height change together — the historical bug where consumers passed `w-10 h-12` and produced an ellipse cannot occur with this API.
|
||||
- Radix manages the image-loading state machine: `<AvatarImage>` is hidden until the image loads successfully, and `<AvatarFallback>` is shown until the image is confirmed loaded. Without a fallback, the avatar surface is empty during the load window and after a load failure.
|
||||
- `<AvatarFallback>` accepts an optional `delayMs` prop (forwarded from Radix) — when set, the fallback is suppressed for that many ms so quick image loads don't cause a brief fallback flash.
|
||||
- The chassis does NOT ship an `AvatarGroup` component — that pattern is consumer composition (`<div className="flex -space-x-2">…</div>` over a list of `<Avatar>`s) and shipping it as a discrete primitive forecloses the many layout variations consumers want.
|
||||
- All sub-components forward refs to the underlying Radix elements so consumers can attach event listeners, focus management, or animation controls without wrapping.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Avatar Variants
|
||||
|
||||
## Purpose
|
||||
|
||||
`avatarVariants` is the cva config that powers the `<Avatar>` chassis component (`<AvatarImage>` and `<AvatarFallback>` ride alongside as sub-component cvas). The root `Avatar` exposes a `size` axis with four rungs (`sm` / `default` / `lg` / `xl`) using Tailwind's `size-*` shorthand so width and height stay in lockstep — this prevents the off-by-one ratio bugs that arise when consumers set width and height independently.
|
||||
|
||||
## Features
|
||||
|
||||
- Single `size` axis with four rungs (`sm` 32px, `default` 40px, `lg` 48px, `xl` 64px) using Tailwind `size-*`.
|
||||
- `rounded-full` applied at the root so the surface is always circular regardless of children.
|
||||
- `overflow-hidden` clips images that would otherwise leak past the circular edge.
|
||||
- `<AvatarImage>` uses `aspect-square h-full w-full object-cover` so portrait / landscape sources crop centrally.
|
||||
- `<AvatarFallback>` uses `bg-muted text-muted-foreground` with `uppercase tracking-wider` so user initials read clearly.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { avatarVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const small = avatarVariants({ size: "sm" });
|
||||
const default_ = avatarVariants();
|
||||
const xl = avatarVariants({ size: "xl" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { cva } from "class-variance-authority";
|
||||
import { avatarVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const ringedAvatar = cva(
|
||||
[avatarVariants({ size: "lg" }), "ring-2 ring-primary ring-offset-2"].join(
|
||||
" "
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `avatarVariants(opts?)`
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `size` | `"sm" \| "default" \| "lg" \| "xl"` | `"default"` | Diameter — uses Tailwind `size-*` to set width and height together. |
|
||||
|
||||
### `avatarImageVariants()`, `avatarFallbackVariants()`
|
||||
|
||||
No options — single canonical class strings.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis uses `size-*` (Tailwind 3.4+) rather than separate `w-*` / `h-*` classes so the avatar's diameter changes in lockstep with the image dimension. This avoids the historical bug where consumers set `w-10 h-12` and produced an ellipse instead of a circle.
|
||||
- `AvatarFallback`'s `bg-muted` background reads as a low-contrast surface that does not compete with the image; the muted-foreground initials are scaled at `text-sm` so even the `xl` size renders the initials at a comfortable read size.
|
||||
- The `relative` positioning on the root container is required by Radix's image-loading state machine — Radix toggles `<AvatarImage>` opacity via inline styles that depend on the parent's positioning context.
|
||||
- `shrink-0` on the root prevents the avatar from shrinking inside flex parents (e.g. inside an `<AvatarGroup>` row or a list item with limited horizontal space).
|
||||
88
docs/ui/react/react-ui/src/components/badge/badge.md
Normal file
88
docs/ui/react/react-ui/src/components/badge/badge.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# Badge
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - badge.variants — `badgeVariants` is the cva config……
|
||||
- [badge.variants](./badge.variants.md) — `badgeVariants` is the cva config that powers the `<Badge>` chassis component. It exposes a `variant` axis with four ru…
|
||||
- badge.variants — `badgeVariants` is the cva config……
|
||||
|
||||
- [badge.variants](./badge.variants.md) — `badgeVariants` is the cva config that powers the `<Badge>` chassis component. It exposes a `variant` axis with four ru…
|
||||
|
||||
- badge.variants — `badgeVariants` is the cva config……
|
||||
|
||||
- [badge.variants](./badge.variants.md) — `badgeVariants` is the cva config that powers the `<Badge>` chassis component. It exposes a `variant` axis with four ru…
|
||||
|
||||
- badge.variants — `badgeVariants` is the cva config…
|
||||
|
||||
- [badge.variants](./badge.variants.md) — `badgeVariants` is the cva config that powers the `<Badge>` chassis component. It exposes a `variant` axis with four ru…
|
||||
- [badge.variants](./badge.variants.md) — `badgeVariants` is the cva config that powers the `<Badge>` chassis component. It exposes a `variant` axis with four ru…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Badge>` is the chassis typographic marker for `@suaveplan/react-ui`. It renders a small inline-flex `<div>` with rounded ends, semibold tracking, and one of four colour variants (`default` / `secondary` / `destructive` / `outline`). Per the shadcn skill rule, `Badge` should be used in place of custom styled spans for status markers, category labels, count indicators, and any other inline emphasis — hand-rolling `<span className="rounded-full bg-green-100 ...">` produces a styled element with no theme awareness, no focus management, and no consistency with the rest of the surface.
|
||||
|
||||
The chassis surface is deliberately minimal: no `size` axis (badges inherit text size from the surrounding context), no internal text (children are consumer-provided), and no icon prop (icons are passed as children alongside a label). For status indicators that pair a semantic colour with iconography, use `<StatusBadge>` instead.
|
||||
|
||||
## Features
|
||||
|
||||
- Four colour variants via the `variant` axis (`default` / `secondary` / `destructive` / `outline`).
|
||||
- `transition-colors` + `motion-reduce:transition-none` so reduced-motion users see instant colour changes.
|
||||
- Focus ring scoped to the badge element so it works when wrapped in an anchor or button.
|
||||
- Hover state on filled variants — outline variant remains decorative.
|
||||
- Forwards refs to the underlying `<div>`.
|
||||
- Theme variant overrides via `useThemedVariants("Badge", badgeVariants)` — a `ThemePlugin` may add brand-new variants (e.g. `variant: "premium"`).
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { Badge } from "@suaveplan/react-ui";
|
||||
|
||||
export function NewLabel() {
|
||||
return <Badge variant="secondary">New</Badge>;
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { Badge } from "@suaveplan/react-ui";
|
||||
import { SparklesIcon } from "lucide-react";
|
||||
|
||||
export function FeatureFlag() {
|
||||
return (
|
||||
<Badge variant="outline" className="gap-1">
|
||||
<SparklesIcon className="size-3" />
|
||||
Beta
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Badge>`
|
||||
|
||||
`forwardRef<HTMLDivElement, BadgeProps>` — renders a `<div>` with the variant classes + consumer-supplied `className` merged via `cn()`.
|
||||
|
||||
#### `BadgeProps`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `variant` | `"default" \| "secondary" \| "destructive" \| "outline"` | `"default"` | Colour treatment — primary fill, secondary fill, destructive fill, or transparent outline. |
|
||||
| _all_ `HTMLAttributes<HTMLDivElement>` | _native_ | _native_ | Forwarded as-is — `aria-label`, `id`, `style`, `data-*`, etc. |
|
||||
|
||||
#### `badgeVariants`
|
||||
|
||||
The bare cva resolver — exported so external consumers (themes, ejected source) can re-use the variants directly.
|
||||
|
||||
#### Errors
|
||||
|
||||
The Badge component does NOT call `useTranslation()` so it does not interact with the i18n provider. It is safe to render outside a `<TranslationProvider>` / `<ReactUiProvider>`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Badges are typographic markers and inherit text size from their parent. The chassis intentionally does NOT expose a `size` axis — consumers wanting larger markers should compose a `<Badge>` inside a sized container or reach for `<StatusBadge>` (which has its own iconography and semantic colour scale).
|
||||
- `inline-flex items-center` is set so badges can contain an icon + label child pair without consumer flex setup. A `gap-1` className is the conventional spacing when an icon is present.
|
||||
- The outline variant intentionally has no `hover:` treatment because it is decorative — applying a hover background would imply interactivity the badge does not have.
|
||||
- The `rounded-full` rule produces the pill shape; combined with the small `px-2.5 py-0.5` and `text-xs font-semibold`, the badge reads as a marker rather than a button.
|
||||
- Because the rendered element is a `<div>`, badges nested inside paragraphs may produce invalid HTML in strict-mode renderers — wrap text-flow badges in a `<span>` (e.g. `<p>Hello <span><Badge>New</Badge></span></p>`). The chassis leaves this composition decision to the consumer rather than hardcoding a `<span>` (which loses the focus ring for `tabIndex={0}` use cases).
|
||||
@@ -0,0 +1,54 @@
|
||||
# Badge Variants
|
||||
|
||||
## Purpose
|
||||
|
||||
`badgeVariants` is the cva config that powers the `<Badge>` chassis component. It exposes a `variant` axis with four rungs (`default` / `secondary` / `destructive` / `outline`) that each pair a background colour with a matching foreground text colour, plus a transparent border for the three filled variants and a foreground-coloured border for the outline variant.
|
||||
|
||||
The variants module is intentionally separate (`badge.variants.ts`) so an ejected source copy under `npx shadcn@latest add @suaveplan/badge` keeps the cva config in a dedicated file, mirroring the upstream shadcn convention.
|
||||
|
||||
## Features
|
||||
|
||||
- Single `variant` axis with four rungs (`default` / `secondary` / `destructive` / `outline`).
|
||||
- `transition-colors` + `motion-reduce:transition-none` so reduced-motion users see instant colour changes.
|
||||
- Focus ring scoped to the badge element so it works as a focusable target when wrapped in an anchor or button.
|
||||
- Hover state for filled variants (`hover:bg-{variant}/80`) — outline variant has no hover treatment because it is decorative.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { badgeVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const primary = badgeVariants();
|
||||
const secondary = badgeVariants({ variant: "secondary" });
|
||||
const danger = badgeVariants({ variant: "destructive" });
|
||||
const outlined = badgeVariants({ variant: "outline" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { cva } from "class-variance-authority";
|
||||
import { badgeVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const auditBadge = cva(
|
||||
[badgeVariants({ variant: "outline" }), "uppercase tracking-wider"].join(" ")
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `badgeVariants(opts?)`
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `variant` | `"default" \| "secondary" \| "destructive" \| "outline"` | `"default"` | Colour treatment — primary fill, secondary fill, destructive fill, or transparent outline. |
|
||||
|
||||
Returns the merged class string.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The default `variant` is `default` (primary fill) — overriding the default at the `defineVariants` level keeps the API consistent with every other chassis component.
|
||||
- The chassis intentionally does NOT expose a `size` axis because badges are typographic markers — they should match the size of the surrounding text. Larger / more emphatic markers should use `<StatusBadge>` (icon + colour combo) or compose a `<Badge>` inside a sized container.
|
||||
- `inline-flex items-center` is applied so badges can contain an icon + label without manual flex setup at the consumer level.
|
||||
- `rounded-full` produces the pill shape that distinguishes badges from buttons or chips.
|
||||
- The outline variant has `text-foreground` with a default `border` (no `border-{colour}`) so it picks up the active theme's border colour automatically.
|
||||
179
docs/ui/react/react-ui/src/components/breadcrumb/breadcrumb.md
Normal file
179
docs/ui/react/react-ui/src/components/breadcrumb/breadcrumb.md
Normal file
@@ -0,0 +1,179 @@
|
||||
# Breadcrumb
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - breadcrumb.variants — `breadcrumbVariants`, `breadc…
|
||||
- [breadcrumb.variants](./breadcrumb.variants.md) — `breadcrumbVariants`, `breadcrumbListVariants`, `breadcrumbItemVariants`, `breadcrumbLinkVariants`, `breadcrumbPageVari…
|
||||
- breadcrumb.variants — `breadcrumbVariants`, `breadc…
|
||||
|
||||
- [breadcrumb.variants](./breadcrumb.variants.md) — `breadcrumbVariants`, `breadcrumbListVariants`, `breadcrumbItemVariants`, `breadcrumbLinkVariants`, `breadcrumbPageVari…
|
||||
|
||||
- breadcrumb.variants — `breadcrumbVariants`, `breadc…
|
||||
|
||||
- [breadcrumb.variants](./breadcrumb.variants.md) — `breadcrumbVariants`, `breadcrumbListVariants`, `breadcrumbItemVariants`, `breadcrumbLinkVariants`, `breadcrumbPageVari…
|
||||
|
||||
- breadcrumb.variants — `breadcrumbVariants`, `breadc…
|
||||
|
||||
- [breadcrumb.variants](./breadcrumb.variants.md) — `breadcrumbVariants`, `breadcrumbListVariants`, `breadcrumbItemVariants`, `breadcrumbLinkVariants`, `breadcrumbPageVari…
|
||||
- [breadcrumb.variants](./breadcrumb.variants.md) — `breadcrumbVariants`, `breadcrumbListVariants`, `breadcrumbItemVariants`, `breadcrumbLinkVariants`, `breadcrumbPageVari…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Breadcrumb>` and its sub-components (`<BreadcrumbList>`, `<BreadcrumbItem>`, `<BreadcrumbLink>`, `<BreadcrumbPage>`, `<BreadcrumbSeparator>`, `<BreadcrumbEllipsis>`) are the chassis breadcrumb-trail primitive. Pure HTML composition — no Radix wrapper — so the markup is deterministic, light, and trivially server-rendered. The wrapper renders a semantic `<nav>` whose accessible name flows through the bundled `react-ui.breadcrumb.label` translation key (override via `ariaLabelKey`). Inside, an `<ol>` carries alternating `<BreadcrumbItem>` (`<li>`) and `<BreadcrumbSeparator>` rows, with the current page rendered via `<BreadcrumbPage>` carrying `aria-current="page"` so screen-readers announce the trail terminus.
|
||||
|
||||
The chassis ships an RTL-safe chevron separator (`rtl:rotate-180`) and a `<BreadcrumbEllipsis>` collapsed-overflow indicator with a translated `aria-label` (`react-ui.breadcrumb.more`). All sub-components forward refs and merge custom `className` values via `cn(...)` chains.
|
||||
|
||||
## Features
|
||||
|
||||
- Semantic `<nav>` + `<ol>` markup so the trail order is implicit in the DOM.
|
||||
- Translated `aria-label` on the wrapper (`react-ui.breadcrumb.label`) so screen-readers announce "Breadcrumb" (or the localised equivalent).
|
||||
- `<BreadcrumbPage>` carries `aria-current="page"` and `role="link"` with no `href` — announced as the current location, not navigable.
|
||||
- `<BreadcrumbLink>` supports `asChild` (Radix Slot) for merging into router-provided `<Link>` components without nested anchors.
|
||||
- `<BreadcrumbSeparator>` defaults to a chevron icon with `rtl:rotate-180`; pass `children` to swap (e.g. a slash).
|
||||
- `<BreadcrumbEllipsis>` provides a collapsed-overflow indicator with a translated `aria-label`.
|
||||
- Theme-driven cva overrides for every slot via `useThemedVariants("Breadcrumb<Slot>")`.
|
||||
- All sub-components forward refs to their underlying HTML elements.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function PageBreadcrumb() {
|
||||
return (
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="/">Home</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="/library">Library</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>Components</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import Link from "next/link";
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbEllipsis,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
interface Crumb {
|
||||
readonly label: string;
|
||||
readonly href?: string;
|
||||
}
|
||||
|
||||
export function CollapsibleBreadcrumb({ crumbs }: { crumbs: Crumb[] }) {
|
||||
const visible =
|
||||
crumbs.length <= 4
|
||||
? crumbs
|
||||
: [crumbs[0], null, ...crumbs.slice(crumbs.length - 2)];
|
||||
|
||||
return (
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
{visible.map((crumb, i) => {
|
||||
if (crumb === null) {
|
||||
return (
|
||||
<BreadcrumbItem key="ellipsis">
|
||||
<BreadcrumbEllipsis />
|
||||
</BreadcrumbItem>
|
||||
);
|
||||
}
|
||||
const isLast = i === visible.length - 1;
|
||||
return (
|
||||
<BreadcrumbItem key={crumb.label}>
|
||||
{isLast || !crumb.href ? (
|
||||
<BreadcrumbPage>{crumb.label}</BreadcrumbPage>
|
||||
) : (
|
||||
<BreadcrumbLink asChild>
|
||||
<Link href={crumb.href}>
|
||||
{crumb.label}
|
||||
</Link>
|
||||
</BreadcrumbLink>
|
||||
)}
|
||||
{!isLast && <BreadcrumbSeparator />}
|
||||
</BreadcrumbItem>
|
||||
);
|
||||
})}
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Breadcrumb>`
|
||||
|
||||
Renders a `<nav>` with a translated `aria-label`.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `ariaLabelKey` | `TranslationKey` | `react-ui.breadcrumb.label` | Override the accessible name. |
|
||||
|
||||
Forwards refs to the underlying `<nav>`.
|
||||
|
||||
### `<BreadcrumbList>`
|
||||
|
||||
Renders an `<ol>` carrying breadcrumb rows. Pure layout; no extra props.
|
||||
|
||||
### `<BreadcrumbItem>`
|
||||
|
||||
Renders an `<li>` row. Pure layout; no extra props.
|
||||
|
||||
### `<BreadcrumbLink>`
|
||||
|
||||
Interactive crumb. Renders an `<a>` by default; merges into the merged child via `asChild`.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `asChild` | `boolean` | `false` | Merge into the rendered child element (e.g. router `<Link>`). |
|
||||
| `href` | `string` | _none_ | Anchor href (ignored when `asChild` is true). |
|
||||
|
||||
### `<BreadcrumbPage>`
|
||||
|
||||
The current-page crumb. Renders a `<span>` with `role="link"`, `aria-current="page"`, and `aria-disabled="true"` so it is announced as the current location while remaining non-navigable.
|
||||
|
||||
### `<BreadcrumbSeparator>`
|
||||
|
||||
The glyph between two crumbs. Renders an `<li role="presentation" aria-hidden="true">`. Defaults to a lucide `ChevronRightIcon` with `rtl:rotate-180`. Pass `children` to swap.
|
||||
|
||||
### `<BreadcrumbEllipsis>`
|
||||
|
||||
Collapsed-overflow indicator. Renders a `<span role="presentation">` with the translated `aria-label`.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `ariaLabelKey` | `TranslationKey` | `react-ui.breadcrumb.more` | Override the accessible name. |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis NEVER wraps the breadcrumb in a Radix primitive — pure HTML keeps SSR trivial and the markup deterministic for SEO crawlers.
|
||||
- The chevron separator carries `rtl:rotate-180` so the trail flows in the reading direction under `dir="rtl"`.
|
||||
- `<BreadcrumbPage>` uses `role="link"` + `aria-disabled="true"` rather than a plain `<span>` so screen-readers and document-outline tools recognise it as part of the navigation set, even though it carries no `href`.
|
||||
- `<BreadcrumbLink asChild>` uses Radix's `Slot` primitive to merge props into the child without a wrapping element — required for router `<Link>` components which would otherwise nest two anchors.
|
||||
- Keys typed as `TranslationKey` flow through `useTranslation()` so consumers can override per-instance without touching the component source.
|
||||
@@ -0,0 +1,85 @@
|
||||
# Breadcrumb Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`breadcrumbVariants`, `breadcrumbListVariants`, `breadcrumbItemVariants`, `breadcrumbLinkVariants`, `breadcrumbPageVariants`, `breadcrumbSeparatorVariants`, and `breadcrumbEllipsisVariants` are the cva (class-variance-authority) configurations powering every `<Breadcrumb>` slot. They are split from `breadcrumb.tsx` into their own module so an ejected source copy under `npx shadcn@latest add @suaveplan/breadcrumb` keeps the cva configurations in a dedicated file. Theme overrides flow through `useThemedVariants("Breadcrumb<Slot>")`.
|
||||
|
||||
The wrapper cva is intentionally empty — breadcrumbs sit in many layout contexts (page header, sticky bar, embedded card) and adding margin / padding here would conflict with the host layout. The list, item, link, page, separator, and ellipsis cvas carry only the classes intrinsic to the breadcrumb itself.
|
||||
|
||||
## Features
|
||||
|
||||
- List: flex row with `gap-1.5 sm:gap-2.5`, wraps on narrow viewports, muted foreground.
|
||||
- Item: inline-flex row with `gap-1.5` between an interactive crumb and a separator.
|
||||
- Link: hover lift to foreground colour, focus-visible ring, smooth-easing colour transition with `motion-reduce:transition-none`.
|
||||
- Page: foreground colour, normal weight (so it does not visually outweigh the page heading).
|
||||
- Separator: sized to `[&>svg]:size-3.5` so the chevron matches the surrounding text.
|
||||
- Ellipsis: `flex h-9 w-9` square so the dots have a clear hit-area.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
breadcrumbLinkVariants,
|
||||
breadcrumbPageVariants,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
const linkClassName = breadcrumbLinkVariants();
|
||||
const pageClassName = breadcrumbPageVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { breadcrumbListVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function BoldBreadcrumbList({
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<ol className={cn(breadcrumbListVariants(), "font-bold", className)}>
|
||||
{children}
|
||||
</ol>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `breadcrumbVariants(): string`
|
||||
|
||||
Returns the wrapper class string (currently empty by design; reserved for theme overrides).
|
||||
|
||||
### `breadcrumbListVariants(): string`
|
||||
|
||||
Returns the `<ol>` class string. No arguments.
|
||||
|
||||
### `breadcrumbItemVariants(): string`
|
||||
|
||||
Returns the `<li>` class string. No arguments.
|
||||
|
||||
### `breadcrumbLinkVariants(): string`
|
||||
|
||||
Returns the `<a>` class string. No arguments.
|
||||
|
||||
### `breadcrumbPageVariants(): string`
|
||||
|
||||
Returns the current-page `<span>` class string. No arguments.
|
||||
|
||||
### `breadcrumbSeparatorVariants(): string`
|
||||
|
||||
Returns the separator `<li>` class string. No arguments.
|
||||
|
||||
### `breadcrumbEllipsisVariants(): string`
|
||||
|
||||
Returns the ellipsis `<span>` class string. No arguments.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- All variants take no axes. Breadcrumb is intentionally simple — variants would multiply test surface area for marginal customisation gain.
|
||||
- The list cva uses `flex-wrap` so long breadcrumb trails wrap gracefully on narrow viewports rather than overflowing.
|
||||
- The link cva uses `focus-visible` (not `focus`) so the ring only appears for keyboard users, not on every mouse click.
|
||||
- `motion-reduce:transition-none` collapses the colour transition for users with `prefers-reduced-motion: reduce`.
|
||||
118
docs/ui/react/react-ui/src/components/button/button.md
Normal file
118
docs/ui/react/react-ui/src/components/button/button.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# Button
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - button.variants — `buttonVariants` is the cva (clas…
|
||||
- [button.variants](./button.variants.md) — `buttonVariants` is the cva (class-variance-authority) configuration that powers every `<Button>` render. It is split f…
|
||||
- button.variants — `buttonVariants` is the cva (clas…
|
||||
|
||||
- [button.variants](./button.variants.md) — `buttonVariants` is the cva (class-variance-authority) configuration that powers every `<Button>` render. It is split f…
|
||||
|
||||
- button.variants — `buttonVariants` is the cva (clas…
|
||||
|
||||
- [button.variants](./button.variants.md) — `buttonVariants` is the cva (class-variance-authority) configuration that powers every `<Button>` render. It is split f…
|
||||
|
||||
- button.variants — `buttonVariants` is the cva (clas…
|
||||
|
||||
- [button.variants](./button.variants.md) — `buttonVariants` is the cva (class-variance-authority) configuration that powers every `<Button>` render. It is split f…
|
||||
- [button.variants](./button.variants.md) — `buttonVariants` is the cva (class-variance-authority) configuration that powers every `<Button>` render. It is split f…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Button>` is the chassis primary action surface for `@suaveplan/react-ui`. It renders a native `<button>` (or polymorphically a child element via Radix `<Slot>` when `asChild` is set), threads its variant resolution through `useThemedVariants("Button")` so an active `ThemePlugin` may extend or replace any variant, and forwards refs to the underlying DOM node so consumer code (focus management, animation libraries, automated testing) can attach to the button directly.
|
||||
|
||||
The component deliberately does NOT carry an `isLoading` or `loading` prop. Per the upstream shadcn convention adopted across `@suaveplan/react-ui`, loading semantics are caller-driven: consumers compose `<Spinner />` + `disabled` + `data-icon="inline-start"` next to the action label rather than letting the button library guess what "loading" means. This keeps `<Button>` a pure styling + a11y surface and lets the caller own the orchestration.
|
||||
|
||||
## Features
|
||||
|
||||
- Six variants (`default`, `secondary`, `destructive`, `outline`, `ghost`, `link`) and four sizes (`sm`, `default`, `lg`, `icon`) shipped in the base cva config.
|
||||
- `asChild` — when `true`, renders `props.children` with the button's classes / refs / props merged in via Radix `<Slot>`. The underlying child MUST be a single React element.
|
||||
- Refs forwarded to the underlying `<button>` (or to the merged child element under `asChild`).
|
||||
- Theme-driven variant overrides via `useThemedVariants("Button", buttonVariants)`. A `ThemePlugin.variantOverrides.Button` may add brand-new variants like `size: "xl"` (see the type-safety trade-off in `proposal.md` §"Type-safety trade-off").
|
||||
- Icon-slot conventions: child `<svg>` elements receive `pointer-events-none size-4 shrink-0` so consumers may drop a Lucide icon next to text without sizing boilerplate.
|
||||
- Accessible-name fallback: when the rendered button has no text children AND the consumer has not supplied `aria-label`, the chassis fills in the translated fallback `react-ui.button.iconOnly.fallback` so screen readers do not announce the empty button.
|
||||
- `prefers-reduced-motion`: hover and focus transitions collapse to instant transitions under `motion-reduce`.
|
||||
- Focus styling via `focus-visible:ring-2 focus-visible:ring-ring` so keyboard users see a visible focus ring; pointer focus does not.
|
||||
- All native `ButtonHTMLAttributes` are forwarded so `onClick`, `disabled`, `form`, `formAction`, etc. work as expected.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { Button } from "@suaveplan/react-ui";
|
||||
|
||||
export function SaveAction() {
|
||||
return (
|
||||
<Button onClick={() => console.warn("save clicked")}>
|
||||
Save changes
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { Button, useThemedVariants } from "@suaveplan/react-ui";
|
||||
import { ChevronRightIcon, MailIcon } from "lucide-react";
|
||||
|
||||
export function ComposedActions() {
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" size="sm">
|
||||
<MailIcon data-icon="inline-start" />
|
||||
Email me
|
||||
</Button>
|
||||
|
||||
<Button variant="destructive" size="lg" disabled>
|
||||
Delete account
|
||||
</Button>
|
||||
|
||||
<Button asChild variant="link">
|
||||
<a href="/settings">Account settings</a>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
aria-label="Open submenu"
|
||||
onClick={(event) => event.preventDefault()}
|
||||
>
|
||||
<ChevronRightIcon />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Button>`
|
||||
|
||||
`forwardRef<HTMLButtonElement, ButtonProps>` — renders a `<button type={type}>` (default `"button"`) when `asChild` is unset, or merges into `props.children` via Radix `<Slot>` when `asChild === true`.
|
||||
|
||||
#### `ButtonProps`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `variant` | `"default" \| "secondary" \| "destructive" \| "outline" \| "ghost" \| "link"` | `"default"` | Visual variant. Layered with `useThemedVariants("Button")` so themes may extend. |
|
||||
| `size` | `"sm" \| "default" \| "lg" \| "icon"` | `"default"` | Padding / height variant. `"icon"` renders a square `size-9` button intended for icon-only content. |
|
||||
| `asChild` | `boolean` | `false` | When `true`, renders `props.children` via Radix `<Slot>` with the button's classes + refs merged in. |
|
||||
| `accessibleNameKey` | `TranslationKey` | `"react-ui.button.iconOnly.fallback"` | Translation key used as the fallback `aria-label` when the button has no text children AND no explicit `aria-label`. |
|
||||
| `type` | `"button" \| "submit" \| "reset"` | `"button"` | Native button type. Suppressed when `asChild` is true (consumer's element owns its `type`). |
|
||||
| _all native_ `ButtonHTMLAttributes<HTMLButtonElement>` | _native_ | _native_ | Forwarded as-is. |
|
||||
|
||||
#### `buttonVariants`
|
||||
|
||||
The bare cva resolver. Exported so external consumers (`@suaveplan/react-ui-theme-*`, ejected source under `npx shadcn@latest add @suaveplan/button`) can re-use the variants directly without going through `useThemedVariants`.
|
||||
|
||||
#### Errors
|
||||
|
||||
- `useTranslation("react-ui")` is called unconditionally inside the component. When the surrounding tree lacks a `<TranslationProvider>` (or `<ReactUiProvider>`), the hook returns its identity-fallback shape and the component renders without throwing — every label resolves to the literal key. This matches the `react-i18n` "no-throw in production" contract.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The variants module is intentionally separate (`button.variants.ts`) so an ejected source copy keeps the cva config in a dedicated file, mirroring the upstream shadcn convention. The chassis component imports it; tree-shaking keeps the footprint identical.
|
||||
- `useThemedVariants("Button", buttonVariants)` is called unconditionally so the hook order is stable across renders. The hook's memoisation key is `(themeName, "Button")` so a re-render with the same theme returns the same resolver reference.
|
||||
- The `hasTextContent` walker is single-level (no recursion) — if a consumer wraps text inside a deeper element tree (`<Button><span><strong>label</strong></span></Button>`), the chassis will still inject the icon-only fallback because it cannot see through `<span>`. This is by design: deeply-nested text-bearing elements should declare their own accessible name on the inner element.
|
||||
- `motion-reduce:transition-none` is hardcoded into the base classes rather than driven by `motionTokens.respectReducedMotion` because the cva-string-vs-CSS-variable boundary makes the variable approach awkward for transition shorthands. The chassis relies on Tailwind's `motion-reduce` variant which honours `prefers-reduced-motion` natively.
|
||||
- `aria-label` precedence: when the consumer supplies `aria-label="…"`, it is preserved verbatim on the rendered element. When omitted AND the children carry no plain text, the icon-only fallback is filled in. When omitted AND text children are present, no `aria-label` is set (the text itself becomes the accessible name).
|
||||
@@ -0,0 +1,57 @@
|
||||
# Button Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`buttonVariants` is the cva (class-variance-authority) configuration that powers every `<Button>` render. It is split from `button.tsx` into its own module so an ejected source copy under `npx shadcn@latest add @suaveplan/button` keeps the cva configuration in a dedicated file — exactly mirroring the upstream shadcn convention. Theme overrides flow through `useThemedVariants("Button", buttonVariants)`, so the runtime cva resolver can be replaced or extended by an active `ThemePlugin` without touching the component source.
|
||||
|
||||
The module exports a single `buttonVariants` callable resolver (`(props?) => string`) tagged with the component name `"Button"` via `defineVariants`. Consumers that want the bare resolver — for example to render the same variants on a non-`<button>` element without going through Radix `<Slot>` — import `buttonVariants` directly and apply the result via `cn()` themselves.
|
||||
|
||||
## Features
|
||||
|
||||
- Six visual variants (`default`, `secondary`, `destructive`, `outline`, `ghost`, `link`) covering the upstream shadcn surface.
|
||||
- Four sizes (`sm`, `default`, `lg`, `icon`) — `icon` renders a square `size-9` button intended for icon-only content.
|
||||
- `data-icon` slot rules so child `<svg>` elements receive `pointer-events-none size-4 shrink-0` automatically.
|
||||
- `motion-reduce:transition-none` so reduced-motion users see no transitions.
|
||||
- `focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2` for keyboard-only focus styling.
|
||||
- `disabled:pointer-events-none disabled:opacity-50` for the native `disabled` state.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { buttonVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const className = buttonVariants({ variant: "default", size: "default" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { buttonVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function PolymorphicLink({ href }: { href: string }) {
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
className={cn(
|
||||
buttonVariants({ variant: "outline", size: "lg" }),
|
||||
"underline-offset-2"
|
||||
)}
|
||||
>
|
||||
Open
|
||||
</a>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `buttonVariants(props?: VariantProps<typeof buttonVariants>): string`
|
||||
|
||||
Returns the cva-merged class string for the supplied variant + size combination. Both fields are optional; omitted fields fall back to the cva `defaultVariants` (`variant: "default"`, `size: "default"`).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The resolver is tagged with `componentName: "Button"` (non-enumerable, non-writable) via `defineVariants` so debug tooling can identify the cva instance at runtime.
|
||||
- The cva config is intentionally exhaustive — every variant × size combination should produce a coherent visual; the chassis test suite asserts each cell renders a non-empty class string.
|
||||
- `motion-reduce:transition-none` is hardcoded into the base classes rather than driven by `motionTokens.respectReducedMotion` because the cva-string-vs-CSS-variable boundary makes the variable approach awkward for transition shorthands. The chassis relies on Tailwind's `motion-reduce` variant which honours `prefers-reduced-motion` natively.
|
||||
- Tokens reference theme-driven CSS custom properties (`bg-primary`, `text-primary-foreground`, `bg-destructive`, `border-input`, etc.) — the actual colour values come from the active `ThemePlugin.cssVariables` payload.
|
||||
105
docs/ui/react/react-ui/src/components/calendar/calendar.md
Normal file
105
docs/ui/react/react-ui/src/components/calendar/calendar.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# Calendar
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - calendar.test-fixtures — Wave-3h local test fixture…
|
||||
- [calendar.test-fixtures](./calendar.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.calendar.*` keyset. The chas…
|
||||
- [calendar.variants](./calendar.variants.md) — `calendarVariants` is the cva config that powers the outer wrapper of `<Calendar>`. The recipe is intentionally minimal…
|
||||
- calendar.test-fixtures — Wave-3h local test fixture…
|
||||
|
||||
- [calendar.test-fixtures](./calendar.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.calendar.*` keyset. The chas…
|
||||
- [calendar.variants](./calendar.variants.md) — `calendarVariants` is the cva config that powers the outer wrapper of `<Calendar>`. The recipe is intentionally minimal…
|
||||
|
||||
- calendar.test-fixtures — Wave-3h local test fixture…
|
||||
|
||||
- [calendar.test-fixtures](./calendar.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.calendar.*` keyset. The chas…
|
||||
- [calendar.variants](./calendar.variants.md) — `calendarVariants` is the cva config that powers the outer wrapper of `<Calendar>`. The recipe is intentionally minimal…
|
||||
|
||||
- calendar.test-fixtures — Wave-3h local test fixture…
|
||||
|
||||
- [calendar.test-fixtures](./calendar.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.calendar.*` keyset. The chas…
|
||||
- [calendar.variants](./calendar.variants.md) — `calendarVariants` is the cva config that powers the outer wrapper of `<Calendar>`. The recipe is intentionally minimal…
|
||||
- [calendar.test-fixtures](./calendar.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.calendar.*` keyset. The chas…
|
||||
- [calendar.variants](./calendar.variants.md) — `calendarVariants` is the cva config that powers the outer wrapper of `<Calendar>`. The recipe is intentionally minimal…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Calendar>` is the chassis date-grid primitive — a thin wrapper over `react-day-picker` (v9) that adds the chassis cva recipe, theme-aware lucide chevrons for previous/next-month navigation, locale-driven `aria-label` overrides, and a labelled outer wrapper. The chassis derives the active locale from `useTranslation()` so date formatting and accessible labels stay in lock-step with the rest of the app surface.
|
||||
|
||||
The chassis exposes the upstream surface verbatim through `Omit<DayPickerProps, "className" | "locale">` — every supported `mode` (`single`, `multiple`, `range`), the `selected` / `onSelect` controlled API, `numberOfMonths`, `showOutsideDays`, `weekStartsOn`, `disabled`, `fromMonth`/`toMonth`, the `modifiers` system, and the rest of the upstream API documented at <https://daypicker.dev>. Two upstream props are intercepted: `className` (so it merges with the chassis recipe via `cn()`) and `locale` (so the chassis can drive it from `useI18n()` rather than the consumer wiring it manually). For the rare case where a single calendar must diverge from the active app locale, pass a `localeOverride` (a `Partial<DayPickerLocale>` per the upstream API).
|
||||
|
||||
## Features
|
||||
|
||||
- Wraps `react-day-picker` v9 with zero additional state — fully controlled or uncontrolled per the upstream API.
|
||||
- Three modes — `single` / `multiple` / `range` — selected via the upstream `mode` prop.
|
||||
- Locale-aware: the chassis reads the active locale from `useTranslation()` so the calendar stays in sync with the app surface.
|
||||
- `aria-label` on the outer wrapper resolves through the active i18n via `labelKey` (defaults to `react-ui.calendar.label`).
|
||||
- Previous / next month navigation buttons get translated `aria-label`s via `previousMonthKey` / `nextMonthKey`.
|
||||
- Lucide-iconography for navigation chevrons (`ChevronLeftIcon` / `ChevronRightIcon`) wired into `react-day-picker`'s `components.Chevron` slot.
|
||||
- Forwards refs to the outer wrapper `<div>`.
|
||||
- All other upstream props pass through unchanged.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { Calendar } from "@suaveplan/react-ui";
|
||||
import { useState } from "react";
|
||||
|
||||
export function ScheduleCalendar() {
|
||||
const [date, setDate] = useState<Date | undefined>(new Date());
|
||||
return <Calendar mode="single" selected={date} onSelect={setDate} />;
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { Calendar } from "@suaveplan/react-ui";
|
||||
import type { DateRange } from "react-day-picker";
|
||||
import { useState } from "react";
|
||||
|
||||
export function BookingCalendar() {
|
||||
const [range, setRange] = useState<DateRange | undefined>();
|
||||
const today = new Date();
|
||||
return (
|
||||
<Calendar
|
||||
mode="range"
|
||||
selected={range}
|
||||
onSelect={setRange}
|
||||
numberOfMonths={2}
|
||||
showOutsideDays
|
||||
disabled={{ before: today }}
|
||||
weekStartsOn={1}
|
||||
previousMonthKey="react-ui.calendar.previousMonth"
|
||||
nextMonthKey="react-ui.calendar.nextMonth"
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Calendar>`
|
||||
|
||||
`forwardRef<HTMLDivElement, CalendarProps>` — renders an outer wrapper `<div role="group" aria-label={…}>` containing the `<DayPicker>` instance.
|
||||
|
||||
#### `CalendarProps`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `mode` | `"single" \| "multiple" \| "range"` | `undefined` | Selection mode — drives the `selected` / `onSelect` shape per the upstream API. |
|
||||
| `labelKey` | `TranslationKey` | `"react-ui.calendar.label"` | `aria-label` for the outer wrapper. |
|
||||
| `previousMonthKey` | `TranslationKey` | `"react-ui.calendar.previousMonth"` | `aria-label` for the previous-month navigation button. |
|
||||
| `nextMonthKey` | `TranslationKey` | `"react-ui.calendar.nextMonth"` | `aria-label` for the next-month navigation button. |
|
||||
| `localeOverride` | `Partial<DayPickerLocale>` | `undefined` | Override the date-fns locale for this calendar instance. When unset, `react-day-picker` falls back to its bundled `enUS`. |
|
||||
| _every other_ `DayPickerProps` field | _upstream_ | _upstream_ | Forwarded as-is. See <https://daypicker.dev>. |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis intentionally does NOT expose the upstream `locale` prop directly — `localeOverride` is a renamed escape hatch that documents the chassis's intent to drive locale from `useTranslation()` by default.
|
||||
- `react-day-picker` ships its own classnames for the grid, day buttons, and navigation chrome via its internal `classNames` map. The chassis only scopes the OUTER container — overriding the inner classnames belongs in a theme variant override.
|
||||
- Keyboard navigation (arrow keys, Home/End, Enter/Space, PageUp/Down) is supplied entirely by `react-day-picker` — the chassis does not wrap or intercept it.
|
||||
- The chevron icons are passed via `components.Chevron` rather than `classNames` so the chassis can swap them for theme-provided lucide / radix icons in a future Wave 5d theme-icon-pack pass.
|
||||
- `react-day-picker` renders its day grid as either a `<table>` (default) or a `[role="grid"]` element depending on the upstream version — the chassis does not assume one or the other in tests, hitting both via the `grid` selector.
|
||||
- When migrating from a previous `<Calendar>` consumer (e.g. an upstream shadcn block), the only required change is to drop the `locale` prop — the chassis derives it automatically.
|
||||
- Per the brief reconciliation note: the chassis casts the unreachable `?? "default"` fallback for `(i18n as { readonly locale: string }).locale` is NOT needed here because the locale is read indirectly via `useTranslation()` rather than from `i18n.locale`.
|
||||
@@ -0,0 +1,63 @@
|
||||
# calendar.test-fixtures
|
||||
|
||||
## Purpose
|
||||
|
||||
Wave-3h local test fixture that augments the chassis test i18n instance with
|
||||
the `react-ui.calendar.*` keyset. The chassis spec tests under
|
||||
`calendar.test.tsx` assert on the resolved English strings ("Calendar",
|
||||
"Previous month", "Next month") before the central wireup commit folds the
|
||||
canonical translations into the bundled `en-GB.json` source-of-truth. Once the
|
||||
wireup commit lands, the bundled keys shadow the fixture and
|
||||
`augmentI18nWithCalendarKeys` becomes a no-op re-merge — the fixture stays in
|
||||
place during the deletion window so the deletion can land as a single atomic
|
||||
cleanup pass without breaking the suite mid-rebase.
|
||||
|
||||
## Features
|
||||
|
||||
- Bundles localized `calendar` strings for all five chassis-supported locales
|
||||
(`en-GB`, `zh-CN`, `ar-AE`, `cy-GB`, `ru-RU`)
|
||||
- Idempotent merge — calling the augment function twice against the same
|
||||
instance is a no-op
|
||||
- Zero coupling to the call-site structure: the augment helper accepts any
|
||||
`I18nInstance`
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
|
||||
```ts
|
||||
import { createTestI18n } from "../../../tests/setup/test-i18n.js";
|
||||
import { augmentI18nWithCalendarKeys } from "./calendar.test-fixtures.js";
|
||||
|
||||
const i18n = await createTestI18n({ locale: "en-GB" });
|
||||
augmentI18nWithCalendarKeys(i18n);
|
||||
```
|
||||
|
||||
### Advanced Example
|
||||
|
||||
```ts
|
||||
import { createTestI18n } from "../../../tests/setup/test-i18n.js";
|
||||
import { augmentI18nWithCalendarKeys } from "./calendar.test-fixtures.js";
|
||||
|
||||
for (const locale of ["en-GB", "ar-AE", "cy-GB"] as const) {
|
||||
const i18n = await createTestI18n({ locale });
|
||||
augmentI18nWithCalendarKeys(i18n);
|
||||
// Assert against the locale-specific resolved strings.
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `augmentI18nWithCalendarKeys(i18n: I18nInstance): void`
|
||||
|
||||
Merges the bundled `calendar` keyset into the supplied i18n instance for every
|
||||
supported locale. Returns nothing. Idempotent.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The fixture is intentionally tiny — five locale × three-key bundles plus a
|
||||
loop. Once the wireup commit folds the canonical keys into the chassis
|
||||
`en-GB.json` source-of-truth, this file will be removed alongside its `.md`
|
||||
sibling, the spec test imports rewritten to drop the
|
||||
`augmentI18nWithCalendarKeys` call, and the change archived as part of the
|
||||
Wave 3h cleanup pass.
|
||||
@@ -0,0 +1,42 @@
|
||||
# Calendar Variants
|
||||
|
||||
## Purpose
|
||||
|
||||
`calendarVariants` is the cva config that powers the outer wrapper of `<Calendar>`. The recipe is intentionally minimal — `react-day-picker` ships its own classnames for the grid, day buttons, and navigation chrome via its internal `classNames` map, so the chassis only needs to scope the OUTER container so it picks up the chassis padding / background / shadow tokens consistently with the other surfaces (Popover, HoverCard, Sheet).
|
||||
|
||||
The variants module is intentionally separate (`calendar.variants.ts`) so an ejected source copy under `npx shadcn@latest add @suaveplan/calendar` keeps the cva config in a dedicated file, mirroring the upstream shadcn convention.
|
||||
|
||||
## Features
|
||||
|
||||
- Single base recipe — no axes (the inner grid styling lives in `react-day-picker`'s own `classNames` map).
|
||||
- Picks up the chassis `bg-popover` / `text-popover-foreground` / `border` / `shadow-sm` tokens so the calendar visually matches the other floating-panel primitives.
|
||||
- `rdp-root` className applied alongside the chassis recipe so consumer CSS can target the wrapper via the upstream selector convention.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { calendarVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const wrapperClasses = calendarVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { calendarVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
const dashboardCalendar = cn(calendarVariants(), "min-w-[320px] shadow-lg");
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `calendarVariants()`
|
||||
|
||||
Returns the merged class string. No options — the recipe has no variant axes.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis intentionally does NOT expose a `size` axis because `react-day-picker` handles its own sizing via `numberOfMonths` and CSS variables on the inner grid. Consumers wanting a wider calendar should pass `numberOfMonths={2 | 3}` to `<Calendar>` directly.
|
||||
- The wrapper carries the same `bg-popover` / `text-popover-foreground` tokens as `<PopoverContent>` because `<DatePicker>` composes a Calendar inside a Popover and the two surfaces should be visually indistinguishable when nested.
|
||||
- `rounded-md border shadow-sm` matches the other surface primitives (Card, Sheet, Drawer) for visual coherence across the chassis.
|
||||
- The `rdp-root` className is exported for parity with the upstream `react-day-picker` API — themes that want to override the inner grid classes can target `[class*='rdp-root'] .rdp-day` etc.
|
||||
135
docs/ui/react/react-ui/src/components/card/card.md
Normal file
135
docs/ui/react/react-ui/src/components/card/card.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# Card
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - card.variants — `cardVariants` (and its sub-compone…
|
||||
- [card.variants](./card.variants.md) — `cardVariants` (and its sub-component cva resolvers `cardHeaderVariants`, `cardTitleVariants`, `cardDescriptionVariants…
|
||||
- card.variants — `cardVariants` (and its sub-compone…
|
||||
|
||||
- [card.variants](./card.variants.md) — `cardVariants` (and its sub-component cva resolvers `cardHeaderVariants`, `cardTitleVariants`, `cardDescriptionVariants…
|
||||
|
||||
- card.variants — `cardVariants` (and its sub-compone…
|
||||
|
||||
- [card.variants](./card.variants.md) — `cardVariants` (and its sub-component cva resolvers `cardHeaderVariants`, `cardTitleVariants`, `cardDescriptionVariants…
|
||||
|
||||
- card.variants — `cardVariants` (and its sub-compone…
|
||||
|
||||
- [card.variants](./card.variants.md) — `cardVariants` (and its sub-component cva resolvers `cardHeaderVariants`, `cardTitleVariants`, `cardDescriptionVariants…
|
||||
- [card.variants](./card.variants.md) — `cardVariants` (and its sub-component cva resolvers `cardHeaderVariants`, `cardTitleVariants`, `cardDescriptionVariants…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Card>` is the chassis composition primitive for content surfaces in `@suaveplan/react-ui`. It ships as a family of six components — `<Card>` (root), `<CardHeader>`, `<CardTitle>`, `<CardDescription>`, `<CardContent>`, `<CardFooter>` — that compose into the conventional shadcn card layout. Per the shadcn skill rule, ALWAYS use the full composition rather than dumping everything into `<CardContent>` — the composition carries semantic structure (heading, description, action footer) that screen-readers, content-extraction tooling, and search summarisation rely on.
|
||||
|
||||
The root `<Card>` exposes a `variant` axis with three rungs (`default` / `outline` / `ghost`) that swap the border + shadow combinations. Sub-components are presentation-only with no exposed axes — they encode conventional spacing (`p-6` on header / content / footer with `pt-0` on the latter two so they sit flush against the header) so consumer composition stays consistent across the chassis.
|
||||
|
||||
## Features
|
||||
|
||||
- Root `<Card>` with `variant` axis (`default` / `outline` / `ghost`).
|
||||
- Six composition primitives — `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter`.
|
||||
- `<CardTitle>` renders as `<h3>` so it nests beneath page-level headings without disrupting the document outline.
|
||||
- `<CardDescription>` renders as `<p>` so the semantic structure stays clean.
|
||||
- All components forward refs to their underlying DOM nodes.
|
||||
- Theme variant overrides via `useThemedVariants` — every sub-component is independently themable.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function TeamCard() {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Team Members</CardTitle>
|
||||
<CardDescription>Manage your team.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>3 active members</CardContent>
|
||||
<CardFooter>Last activity 2 hours ago</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function PlanCard({ name, price }: { name: string; price: number }) {
|
||||
return (
|
||||
<Card variant="outline" className="max-w-sm">
|
||||
<CardHeader>
|
||||
<CardTitle>{name}</CardTitle>
|
||||
<CardDescription>Pay annually, cancel any time.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<span className="text-3xl font-bold">${price}</span>
|
||||
<span className="text-sm text-muted-foreground">/mo</span>
|
||||
</CardContent>
|
||||
<CardFooter className="gap-2">
|
||||
<Button variant="outline">Compare</Button>
|
||||
<Button>Upgrade</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Card>`
|
||||
|
||||
`forwardRef<HTMLDivElement, CardProps>` — root container.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `variant` | `"default" \| "outline" \| "ghost"` | `"default"` | Border + shadow combination. |
|
||||
| _all_ `HTMLAttributes<HTMLDivElement>` | _native_ | _native_ | Forwarded as-is. |
|
||||
|
||||
### `<CardHeader>`
|
||||
|
||||
`forwardRef<HTMLDivElement, CardHeaderProps>` — flex column wrapping `<CardTitle>` and `<CardDescription>`.
|
||||
|
||||
### `<CardTitle>`
|
||||
|
||||
`forwardRef<HTMLHeadingElement, CardTitleProps>` — heading text rendered as `<h3>`.
|
||||
|
||||
### `<CardDescription>`
|
||||
|
||||
`forwardRef<HTMLParagraphElement, CardDescriptionProps>` — supplementary description rendered as `<p>`.
|
||||
|
||||
### `<CardContent>`
|
||||
|
||||
`forwardRef<HTMLDivElement, CardContentProps>` — main content body. Pad-without-top.
|
||||
|
||||
### `<CardFooter>`
|
||||
|
||||
`forwardRef<HTMLDivElement, CardFooterProps>` — flex row for footer actions. Pad-without-top.
|
||||
|
||||
#### Errors
|
||||
|
||||
The Card components do NOT call `useTranslation()` so they do not interact with the i18n provider. They are safe to render outside a `<TranslationProvider>` / `<ReactUiProvider>`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Card sub-components are deliberately separate exports rather than namespaced object members (`Card.Header`, `Card.Title`, etc.) — this keeps tree-shaking trivial and matches the 5-files-per-feature convention.
|
||||
- `<CardTitle>` defaults to `<h3>` so it nests under page-level `<h1>` and `<h2>` without disrupting the document outline. Consumers needing a different heading level should compose their own heading element and apply `cardTitleVariants()` manually.
|
||||
- `<CardDescription>` renders as `<p>` rather than a `<div>` so screen-readers announce it as paragraph text rather than a generic container.
|
||||
- Padding is `p-6` everywhere; the header sets the top padding, then content / footer apply `pt-0` to sit flush. This produces consistent spacing whether or not all sub-components are present.
|
||||
- The `ghost` variant strips both border and shadow, useful when the card sits inside another bordered surface and the double-border would look heavy.
|
||||
54
docs/ui/react/react-ui/src/components/card/card.variants.md
Normal file
54
docs/ui/react/react-ui/src/components/card/card.variants.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Card Variants
|
||||
|
||||
## Purpose
|
||||
|
||||
`cardVariants` (and its sub-component cva resolvers `cardHeaderVariants`, `cardTitleVariants`, `cardDescriptionVariants`, `cardContentVariants`, `cardFooterVariants`) drive the chassis `<Card>` composition. The root `Card` exposes a `variant` axis with three rungs (`default` / `outline` / `ghost`) that swap the border + shadow combinations. The sub-component cvas are presentation-only with no exposed axes — they encode the conventional spacing (`p-6` for header / content / footer with `pt-0` on content / footer to sit flush against the header) so consumer composition stays consistent across the chassis.
|
||||
|
||||
The variants module is intentionally separate (`card.variants.ts`) so an ejected source copy under `npx shadcn@latest add @suaveplan/card` keeps the cva config in a dedicated file.
|
||||
|
||||
## Features
|
||||
|
||||
- Root `variant` axis with `default` (border + shadow), `outline` (border, no shadow), and `ghost` (no border, no shadow).
|
||||
- Sub-component cvas encode conventional spacing — `p-6` on header / content / footer with `pt-0` on the latter two.
|
||||
- Title is `text-2xl font-semibold leading-none tracking-tight` — matching shadcn's typographic rhythm.
|
||||
- Description is `text-sm text-muted-foreground` — visually distinct from the title.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { cardVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const default_ = cardVariants();
|
||||
const outlined = cardVariants({ variant: "outline" });
|
||||
const ghost = cardVariants({ variant: "ghost" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { cva } from "class-variance-authority";
|
||||
import { cardVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const featuredCard = cva(
|
||||
[cardVariants({ variant: "default" }), "ring-2 ring-primary"].join(" ")
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `cardVariants(opts?)`
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `variant` | `"default" \| "outline" \| "ghost"` | `"default"` | Border + shadow combination. |
|
||||
|
||||
### `cardHeaderVariants()`, `cardTitleVariants()`, `cardDescriptionVariants()`, `cardContentVariants()`, `cardFooterVariants()`
|
||||
|
||||
No options — single canonical class string per resolver.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The `ghost` variant is useful when the card sits inside another bordered surface (e.g. a `<Sheet>` or another card) — the absent border avoids the visual "double-border" effect.
|
||||
- Padding is `p-6` to match shadcn's conventional rhythm. Consumers needing tighter padding override at the call site via `className="p-4"`.
|
||||
- `cardContentVariants` and `cardFooterVariants` apply `pt-0` (no top padding) so the header padding is the only top spacing — pairing with the header's `p-6` keeps the spacing rhythm consistent regardless of which sub-components are present.
|
||||
- `<CardTitle>` defaults to `<h3>` so it nests beneath `<h1>` / `<h2>` headings without disrupting the document outline. Consumers wanting a different heading level override at the call site by composing with their own heading element and applying `cardTitleVariants()` manually.
|
||||
148
docs/ui/react/react-ui/src/components/checkbox/checkbox.md
Normal file
148
docs/ui/react/react-ui/src/components/checkbox/checkbox.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# Checkbox
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - checkbox.variants — `checkboxVariants` is the cva (…
|
||||
- [checkbox.variants](./checkbox.variants.md) — `checkboxVariants` is the cva (class-variance-authority) configuration that powers every `<Checkbox>` render. It is spl…
|
||||
- checkbox.variants — `checkboxVariants` is the cva (…
|
||||
|
||||
- [checkbox.variants](./checkbox.variants.md) — `checkboxVariants` is the cva (class-variance-authority) configuration that powers every `<Checkbox>` render. It is spl…
|
||||
|
||||
- checkbox.variants — `checkboxVariants` is the cva (…
|
||||
|
||||
- [checkbox.variants](./checkbox.variants.md) — `checkboxVariants` is the cva (class-variance-authority) configuration that powers every `<Checkbox>` render. It is spl…
|
||||
|
||||
- checkbox.variants — `checkboxVariants` is the cva (…
|
||||
|
||||
- [checkbox.variants](./checkbox.variants.md) — `checkboxVariants` is the cva (class-variance-authority) configuration that powers every `<Checkbox>` render. It is spl…
|
||||
- [checkbox.variants](./checkbox.variants.md) — `checkboxVariants` is the cva (class-variance-authority) configuration that powers every `<Checkbox>` render. It is spl…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Checkbox>` is the chassis form-checkbox primitive built on `@radix-ui/react-checkbox`. Radix supplies the a11y semantics (`role="checkbox"`, `aria-checked` reflecting `true | false | "mixed"`, native `Space` activation, `disabled` propagation); the chassis layers theme-driven variant resolution, the indicator render, and the cva `size` axis so consumers can scale checkboxes inside data-table headers, settings panes, and dense forms without manual class overrides.
|
||||
|
||||
The indicator picks its icon based on the controlled `checked` value: `<CheckIcon>` for the boolean-true case and `<MinusIcon>` for the indeterminate case. Both icons carry a `data-icon` slot attribute so consumer themes may swap them. Refs are forwarded to the Radix Root which renders a styled `<button>`.
|
||||
|
||||
Icon-only checkboxes (no visible label sibling) MUST supply an `aria-label` so screen readers can announce the control. The chassis does NOT inject a fallback because the check icon's purpose is domain-specific — only the consumer knows what is being toggled.
|
||||
|
||||
## Features
|
||||
|
||||
- Three sizes (`sm`, `default`, `lg`) scaling the box and inner icon proportionally.
|
||||
- Indicator render with `<CheckIcon>` for the checked state and `<MinusIcon>` for the indeterminate state — both with `data-icon` slot attribution.
|
||||
- Controlled and uncontrolled use via Radix `checked` / `defaultChecked` / `onCheckedChange`.
|
||||
- `aria-checked="mixed"` for the indeterminate state per WAI-ARIA spec.
|
||||
- `disabled` blocks pointer + keyboard input and decays opacity.
|
||||
- `data-state="checked|unchecked|indeterminate"` mirrored on the rendered button so consumer cva configs / CSS can react.
|
||||
- Refs forwarded to the Radix Root.
|
||||
- `motion-reduce:transition-none` collapses transitions for `prefers-reduced-motion: reduce`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { Checkbox } from "@suaveplan/react-ui";
|
||||
|
||||
export function AcceptTerms() {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox id="terms" />
|
||||
<label htmlFor="terms">Accept terms</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { Checkbox } from "@suaveplan/react-ui";
|
||||
import { useMemo, useState } from "react";
|
||||
|
||||
interface Permission {
|
||||
readonly id: string;
|
||||
readonly label: string;
|
||||
}
|
||||
|
||||
export function PermissionMatrix({ items }: { items: readonly Permission[] }) {
|
||||
const [selected, setSelected] = useState<readonly string[]>([]);
|
||||
|
||||
const allChecked = selected.length === items.length;
|
||||
const someChecked = selected.length > 0 && !allChecked;
|
||||
const headerState = allChecked ? true : someChecked ? "indeterminate" : false;
|
||||
|
||||
const toggleAll = () =>
|
||||
setSelected(allChecked ? [] : items.map((i) => i.id));
|
||||
const toggleOne = (id: string) =>
|
||||
setSelected((prev) =>
|
||||
prev.includes(id) ? prev.filter((p) => p !== id) : [...prev, id]
|
||||
);
|
||||
|
||||
return (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<Checkbox
|
||||
aria-label="select all"
|
||||
checked={headerState}
|
||||
onCheckedChange={toggleAll}
|
||||
/>
|
||||
</th>
|
||||
<th>Permission</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{items.map((item) => (
|
||||
<tr key={item.id}>
|
||||
<td>
|
||||
<Checkbox
|
||||
aria-label={`select ${item.label}`}
|
||||
checked={selected.includes(item.id)}
|
||||
onCheckedChange={() => toggleOne(item.id)}
|
||||
/>
|
||||
</td>
|
||||
<td>{item.label}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Checkbox>`
|
||||
|
||||
`forwardRef<ComponentRef<typeof CheckboxPrimitive.Root>, CheckboxProps>` — wraps Radix Root, renders a `<button role="checkbox">` containing the Radix Indicator with the appropriate icon.
|
||||
|
||||
#### `CheckboxProps`
|
||||
|
||||
Inherits every prop from Radix `CheckboxPrimitive.Root` except `asChild`. Adds:
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Visual variant scaling box + icon. |
|
||||
|
||||
Common Radix props consumers will use:
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `checked` | `boolean \| "indeterminate"` | _uncontrolled_ | Controlled state. |
|
||||
| `defaultChecked` | `boolean` | `false` | Initial state for uncontrolled use. |
|
||||
| `onCheckedChange` | `(checked: boolean \| "indeterminate") => void` | _none_ | Fires on state change. |
|
||||
| `disabled` | `boolean` | `false` | Blocks input + decays opacity. |
|
||||
| `required` | `boolean` | `false` | Native form-field `required`. |
|
||||
| `name` | `string` | _none_ | Native form-field name. |
|
||||
| `value` | `string` | `"on"` | Native form-field value when checked. |
|
||||
|
||||
#### `checkboxVariants`
|
||||
|
||||
The bare cva resolver. Exported so external consumers can re-use the variants directly.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The variants module is intentionally separate (`checkbox.variants.ts`) so an ejected source copy keeps the cva config in a dedicated file.
|
||||
- The indicator uses `<CheckIcon>` / `<MinusIcon>` from `lucide-react` rather than inline SVG so consumers replacing the icon library via `ThemePlugin.iconLibrary` get a coherent swap surface.
|
||||
- The chassis does NOT auto-inject an `aria-label` for icon-only use — see `Purpose` for the rationale.
|
||||
- The `data-state` attribute is supplied by Radix; the chassis cva config keys `data-[state=checked]:bg-primary` / `data-[state=indeterminate]:bg-primary` off it so checked + indeterminate render with the same primary background.
|
||||
- Refs target the Radix Root (the `<button>` element) — consumers can `.focus()` the checkbox programmatically through the forwarded ref.
|
||||
@@ -0,0 +1,53 @@
|
||||
# Checkbox Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`checkboxVariants` is the cva (class-variance-authority) configuration that powers every `<Checkbox>` render. It is split from `checkbox.tsx` into its own module so an ejected source copy under `npx shadcn@latest add @suaveplan/checkbox` keeps the cva configuration in a dedicated file. Theme overrides flow through `useThemedVariants("Checkbox", checkboxVariants)`, so the runtime cva resolver can be replaced or extended by an active `ThemePlugin` without touching the component source.
|
||||
|
||||
The module exports a single `checkboxVariants` callable resolver tagged with the component name `"Checkbox"` via `defineVariants`.
|
||||
|
||||
## Features
|
||||
|
||||
- Three sizes (`sm` → `h-3.5 w-3.5`, `default` → `h-4 w-4`, `lg` → `h-5 w-5`) scaling the box.
|
||||
- `border-primary` border + `shadow` so the box reads against muted backgrounds.
|
||||
- `data-[state=checked]:bg-primary` + `data-[state=indeterminate]:bg-primary` flip the fill when checked or partial.
|
||||
- `disabled:cursor-not-allowed disabled:opacity-50` for the native disabled state.
|
||||
- `motion-reduce:transition-none` for users with `prefers-reduced-motion: reduce`.
|
||||
- `focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2` for keyboard-only focus styling.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { checkboxVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const className = checkboxVariants({ size: "default" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { checkboxVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function InlineCheckbox({ extra }: { extra?: string }) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
role="checkbox"
|
||||
aria-checked="false"
|
||||
className={cn(checkboxVariants({ size: "sm" }), extra)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `checkboxVariants(props?: { size?: "sm" | "default" | "lg" }): string`
|
||||
|
||||
Returns the cva-merged class string for the supplied size. Omitted props fall back to the cva `defaultVariants` (`size: "default"`).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The resolver is tagged with `componentName: "Checkbox"` (non-enumerable, non-writable) via `defineVariants`.
|
||||
- The cva config exposes only the `size` axis. Theme-specific variants (rounded vs square, filled vs outlined) are layered via `variantOverrides.Checkbox` rather than baked into the chassis.
|
||||
- Tokens reference theme-driven CSS custom properties (`border-primary`, `bg-primary`, `text-primary-foreground`) so the colour palette comes from the active `ThemePlugin.cssVariables` payload.
|
||||
123
docs/ui/react/react-ui/src/components/collapsible/collapsible.md
Normal file
123
docs/ui/react/react-ui/src/components/collapsible/collapsible.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# Collapsible
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - collapsible.variants — `collapsibleVariants`, `coll…
|
||||
- [collapsible.variants](./collapsible.variants.md) — `collapsibleVariants`, `collapsibleTriggerVariants`, and `collapsibleContentVariants` are the cva (class-variance-autho…
|
||||
- collapsible.variants — `collapsibleVariants`, `coll…
|
||||
|
||||
- [collapsible.variants](./collapsible.variants.md) — `collapsibleVariants`, `collapsibleTriggerVariants`, and `collapsibleContentVariants` are the cva (class-variance-autho…
|
||||
|
||||
- collapsible.variants — `collapsibleVariants`, `coll…
|
||||
|
||||
- [collapsible.variants](./collapsible.variants.md) — `collapsibleVariants`, `collapsibleTriggerVariants`, and `collapsibleContentVariants` are the cva (class-variance-autho…
|
||||
|
||||
- collapsible.variants — `collapsibleVariants`, `coll…
|
||||
|
||||
- [collapsible.variants](./collapsible.variants.md) — `collapsibleVariants`, `collapsibleTriggerVariants`, and `collapsibleContentVariants` are the cva (class-variance-autho…
|
||||
- [collapsible.variants](./collapsible.variants.md) — `collapsibleVariants`, `collapsibleTriggerVariants`, and `collapsibleContentVariants` are the cva (class-variance-autho…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Collapsible>`, `<CollapsibleTrigger>`, and `<CollapsibleContent>` are the chassis single-section disclosure primitives built on `@radix-ui/react-collapsible`. Radix supplies the WAI-ARIA disclosure semantics: the trigger renders a `<button>` with `aria-expanded` reflecting the open state and `aria-controls` referencing the content panel; the content carries `role="region"` (when given an accessible name) and unmounts while closed. The chassis layers a small cva recipe for structural alignment plus a `data-[state=open]:rotate-180` hook so a sibling chevron icon flips when the panel opens.
|
||||
|
||||
Use this primitive for ONE open/closed pair (e.g. an "advanced settings" disclosure or an FAQ item that does not belong to a sibling-managed group). For sibling panels with mutual-exclusion (`type="single"`) or multi-open (`type="multiple"`) semantics, use `<Accordion>` instead.
|
||||
|
||||
## Features
|
||||
|
||||
- WAI-ARIA disclosure semantics auto-managed by Radix (trigger `aria-expanded` / `aria-controls`, content `role="region"` when titled).
|
||||
- Keyboard activation: Space and Enter both toggle the trigger.
|
||||
- Controlled (`open` / `onOpenChange`) and uncontrolled (`defaultOpen`) modes.
|
||||
- `disabled` prop short-circuits the toggle while keeping the trigger focusable for parity with `<button disabled>`.
|
||||
- Smooth open/close animation via `--radix-collapsible-content-height` (`animate-collapsible-down/up` keyframes); collapses to none under `prefers-reduced-motion: reduce`.
|
||||
- Theme-driven cva overrides for `Collapsible`, `CollapsibleTrigger`, and `CollapsibleContent`.
|
||||
- All sub-components forward refs to their underlying Radix elements.
|
||||
- Trigger cva carries focus-visible ring + hover-bg recipes so the disclosure looks like the chassis Button family without being one.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function AdvancedOptions() {
|
||||
return (
|
||||
<Collapsible>
|
||||
<CollapsibleTrigger>Advanced options</CollapsibleTrigger>
|
||||
<CollapsibleContent>
|
||||
<p>Hidden content revealed when toggled.</p>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Button,
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from "@suaveplan/react-ui";
|
||||
import { ChevronDownIcon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
export function FilterPanel({ children }: { children: React.ReactNode }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<Collapsible open={open} onOpenChange={setOpen}>
|
||||
<CollapsibleTrigger asChild>
|
||||
<Button variant="outline" className="w-full justify-between">
|
||||
{open ? "Hide filters" : "Show filters"}
|
||||
<ChevronDownIcon
|
||||
data-icon="chevron"
|
||||
className="size-4 transition-transform data-[state=open]:rotate-180"
|
||||
aria-hidden
|
||||
/>
|
||||
</Button>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent className="mt-3 rounded-md border p-3">
|
||||
{children}
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Collapsible>`
|
||||
|
||||
Renders the Radix Root that owns the open/closed state machine.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `open` | `boolean` | _uncontrolled_ | Controlled open state. |
|
||||
| `defaultOpen` | `boolean` | `false` | Initial open state for uncontrolled use. |
|
||||
| `onOpenChange` | `(open: boolean) => void` | _none_ | Fires on every open / close transition. |
|
||||
| `disabled` | `boolean` | `false` | When true, the trigger no longer toggles state. |
|
||||
|
||||
### `<CollapsibleTrigger>`
|
||||
|
||||
Wraps the Radix Trigger; renders a `<button>` (or polymorphically the child via `asChild`).
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `asChild` | `boolean` | `false` | Merge the chassis trigger props onto the rendered child instead of wrapping in a `<button>`. Useful for binding to a `<Button>`. |
|
||||
|
||||
### `<CollapsibleContent>`
|
||||
|
||||
Wraps the Radix Content. Children unmount while closed (Radix default).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis NEVER assigns a numeric `z-index` (per the shadcn skill rule); the disclosure stays in flow and stacking is implicit.
|
||||
- The animation classes (`animate-collapsible-down/up`) require the chassis Tailwind preset to register the corresponding keyframes (shipped in `@suaveplan/react-ui/styles/global.css`).
|
||||
- Radix unmounts content when closed by default; render a wrapper around the trigger if you need persistent layout (e.g. a `<section>` element that always appears in the DOM).
|
||||
- The trigger cva matches the chassis `<Button variant="ghost">` recipe so a disclosure used alone looks like a Button without being one. Use `asChild` to literally render a `<Button>` when consistency with the surrounding button cluster matters.
|
||||
- `disabled` flows through to the underlying `<button>` so screen-readers announce the disabled state correctly via the native attribute.
|
||||
@@ -0,0 +1,70 @@
|
||||
# Collapsible Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`collapsibleVariants`, `collapsibleTriggerVariants`, and `collapsibleContentVariants` are the cva (class-variance-authority) configurations powering every `<Collapsible>` slot. They are split from `collapsible.tsx` into their own module so an ejected source copy under `npx shadcn@latest add @suaveplan/collapsible` keeps the cva configurations in a dedicated file. Theme overrides flow through `useThemedVariants("Collapsible<Slot>")`.
|
||||
|
||||
The root cva is intentionally minimal (`flex flex-col`) so the disclosure groups its trigger and content as a column. The trigger cva matches the chassis Button-ghost recipe so a stand-alone disclosure looks consistent with surrounding buttons. The content cva carries the open/close animation hooks driven by Radix's `data-state` attribute.
|
||||
|
||||
## Features
|
||||
|
||||
- Root: `flex flex-col` so the trigger and content stack vertically by default.
|
||||
- Trigger: full-width flex row with `items-center justify-between gap-2`, hover-bg, focus-visible ring, disabled-opacity, and `motion-reduce:transition-none`.
|
||||
- Content: `overflow-hidden text-sm` plus `data-[state=open]:animate-collapsible-down` / `data-[state=closed]:animate-collapsible-up` so Radix's `--radix-collapsible-content-height` CSS variable animates cleanly. `motion-reduce:animate-none` collapses the transition under `prefers-reduced-motion: reduce`.
|
||||
- NEVER carries a numeric `z-index` (the disclosure stays in flow).
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
collapsibleContentVariants,
|
||||
collapsibleTriggerVariants,
|
||||
collapsibleVariants,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
const rootClassName = collapsibleVariants();
|
||||
const triggerClassName = collapsibleTriggerVariants();
|
||||
const contentClassName = collapsibleContentVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { collapsibleTriggerVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomTrigger() {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
aria-expanded
|
||||
className={cn(
|
||||
collapsibleTriggerVariants(),
|
||||
"border border-primary text-primary"
|
||||
)}
|
||||
>
|
||||
Toggle
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `collapsibleVariants(): string`
|
||||
|
||||
Returns the root grouping classes; takes no arguments.
|
||||
|
||||
### `collapsibleTriggerVariants(): string`
|
||||
|
||||
Returns the trigger button classes; takes no arguments.
|
||||
|
||||
### `collapsibleContentVariants(): string`
|
||||
|
||||
Returns the content panel classes; takes no arguments.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The trigger uses `focus-visible:ring-2 focus-visible:ring-ring` instead of `focus:ring-2` so mouse-only consumers do not see a focus ring on click — this matches the chassis Button family.
|
||||
- The content animation requires the `animate-collapsible-down/up` keyframes registered in the chassis Tailwind preset; consumers ejecting the source must keep those keyframes in their own Tailwind config (or register them via `tailwindcss-animate`'s `accordion-down/up` recipe under matching names).
|
||||
- Per the shadcn skill rule, the cva NEVER assigns a numeric `z-index`. The disclosure renders inline in the document flow.
|
||||
- The trigger's `gap-2` class assumes a single icon child (the chevron) sits next to the label; consumers can override via `cn(...)` when an alternate layout is required.
|
||||
@@ -0,0 +1,172 @@
|
||||
# `<ConfirmDeleteDialog>`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - confirm-delete-dialog.test-fixtures — Wave-3g local…
|
||||
- [confirm-delete-dialog.test-fixtures](./confirm-delete-dialog.test-fixtures.md) — Wave-3g local test fixture that augments the chassis test i18n instance with the `react-ui.confirmDelete.*` keyset. The…
|
||||
- confirm-delete-dialog.test-fixtures — Wave-3g local…
|
||||
|
||||
- [confirm-delete-dialog.test-fixtures](./confirm-delete-dialog.test-fixtures.md) — Wave-3g local test fixture that augments the chassis test i18n instance with the `react-ui.confirmDelete.*` keyset. The…
|
||||
|
||||
- confirm-delete-dialog.test-fixtures — Wave-3g local…
|
||||
|
||||
- [confirm-delete-dialog.test-fixtures](./confirm-delete-dialog.test-fixtures.md) — Wave-3g local test fixture that augments the chassis test i18n instance with the `react-ui.confirmDelete.*` keyset. The…
|
||||
|
||||
- confirm-delete-dialog.test-fixtures — Wave-3g local…
|
||||
|
||||
- [confirm-delete-dialog.test-fixtures](./confirm-delete-dialog.test-fixtures.md) — Wave-3g local test fixture that augments the chassis test i18n instance with the `react-ui.confirmDelete.*` keyset. The…
|
||||
- [confirm-delete-dialog.test-fixtures](./confirm-delete-dialog.test-fixtures.md) — Wave-3g local test fixture that augments the chassis test i18n instance with the `react-ui.confirmDelete.*` keyset. The…
|
||||
|
||||
## Purpose
|
||||
|
||||
Chassis delete-confirmation dialog built atop `<AlertDialog>` (Wave
|
||||
3c). shadcn does not ship a delete-confirmation recipe upstream;
|
||||
consumers conventionally hand-roll one per surface. The chassis
|
||||
exposes this composition so destructive confirmations across the
|
||||
product share the same wording, button styling, accessibility
|
||||
contract, and i18n keyset.
|
||||
|
||||
## Features
|
||||
|
||||
- Wraps `<AlertDialog>` with delete-confirmation defaults — no
|
||||
additional Radix wiring, no fresh focus-trap logic.
|
||||
- Controlled visibility — the parent owns `isOpen`; the chassis fires
|
||||
`onConfirm` / `onCancel` callbacks.
|
||||
- Modal — clicking the overlay does NOT dismiss (inherited from
|
||||
`<AlertDialog>`'s click-outside suppression).
|
||||
- ALWAYS renders an `<AlertDialogDescription>` so AlertDialog never
|
||||
emits its dev-mode missing-description warning (Wave 3c finding #5).
|
||||
- Title and confirm-button text deliberately differ so test queries
|
||||
do not multi-match a single rendered surface (Wave 3c finding #6).
|
||||
- Branded `TranslationKey` fields for every visible string; raw
|
||||
`string` accepted on `entityName` so user-supplied names (vault
|
||||
item title, document file name) flow through verbatim.
|
||||
- `MessageParser`-driven interpolation for the title template
|
||||
(`Delete {entityName}?`).
|
||||
- Destructive button styling (`bg-destructive` palette) so the
|
||||
confirm CTA reads as a "danger" action.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { ConfirmDeleteDialog } from "@suaveplan/react-ui";
|
||||
import { useState } from "react";
|
||||
|
||||
function VaultItemRow(): JSX.Element {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<button type="button" onClick={() => setIsOpen(true)}>
|
||||
Delete
|
||||
</button>
|
||||
<ConfirmDeleteDialog
|
||||
isOpen={isOpen}
|
||||
entityName="My password"
|
||||
onConfirm={() => {
|
||||
deleteItem();
|
||||
setIsOpen(false);
|
||||
}}
|
||||
onCancel={() => setIsOpen(false)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { ConfirmDeleteDialog } from "@suaveplan/react-ui";
|
||||
import { useState } from "react";
|
||||
|
||||
function ProjectAdminPanel(): JSX.Element {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [pendingId, setPendingId] = useState<string | null>(null);
|
||||
|
||||
const onConfirm = async () => {
|
||||
if (pendingId === null) return;
|
||||
await deleteProject(pendingId);
|
||||
setPendingId(null);
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setPendingId("proj_42");
|
||||
setIsOpen(true);
|
||||
}}
|
||||
>
|
||||
Delete project
|
||||
</button>
|
||||
<ConfirmDeleteDialog
|
||||
isOpen={isOpen}
|
||||
entityName="vault.item.password"
|
||||
titleKey="react-ui.confirmDelete.title"
|
||||
description="react-ui.confirmDelete.description"
|
||||
confirmLabel="react-ui.confirmDelete.confirm"
|
||||
cancelLabel="react-ui.confirmDelete.cancel"
|
||||
size="sm"
|
||||
onConfirm={onConfirm}
|
||||
onCancel={() => {
|
||||
setPendingId(null);
|
||||
setIsOpen(false);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<ConfirmDeleteDialog>` props
|
||||
|
||||
- `isOpen: boolean` — REQUIRED. Controlled visibility.
|
||||
- `onConfirm: () => void` — REQUIRED. Fired when the destructive
|
||||
action button is activated.
|
||||
- `onCancel: () => void` — REQUIRED. Fired on the cancel button OR on
|
||||
Escape.
|
||||
- `entityName: TranslationKey | string` — REQUIRED. Name of the
|
||||
thing being deleted. Branded keys are routed through `t()`; raw
|
||||
strings are forwarded verbatim. The default title interpolates this
|
||||
value via `MessageParser` (`Delete {entityName}?`).
|
||||
- `confirmLabel?: TranslationKey` — defaults to
|
||||
`react-ui.confirmDelete.confirm` ("Delete").
|
||||
- `cancelLabel?: TranslationKey` — defaults to
|
||||
`react-ui.confirmDelete.cancel` ("Cancel").
|
||||
- `description?: TranslationKey` — defaults to
|
||||
`react-ui.confirmDelete.description` ("This action cannot be
|
||||
undone.").
|
||||
- `titleKey?: TranslationKey` — defaults to
|
||||
`react-ui.confirmDelete.title` (`Delete {entityName}?`).
|
||||
- `size?: 'sm' | 'default' | 'lg' | 'xl'` — forwarded to the
|
||||
underlying `<AlertDialogContent size>` cva slot. Defaults to
|
||||
AlertDialog's default (`'default'`).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis discriminates `entityName` between branded key and raw
|
||||
string heuristically: dotted-lowercase-namespace strings are
|
||||
treated as keys; everything else is rendered verbatim. This is
|
||||
safe-by-degradation — a misclassified raw string falls through
|
||||
`t()` which returns the input on a missing key.
|
||||
- `MessageParser` is constructed via `useMemo` so the same instance
|
||||
is reused across renders for cheaper plural / select handling on
|
||||
re-localised templates.
|
||||
- The destructive button styling (`bg-destructive ...`) is layered
|
||||
atop the `<AlertDialogAction>` cva slot via `className` rather than
|
||||
introducing a new variant — this keeps the AlertDialog cva surface
|
||||
small and signals "destructive" via the inline-class override.
|
||||
- The component does NOT auto-close on confirm/cancel — the parent
|
||||
callback owns the close transition. This keeps the lifecycle
|
||||
parent-driven and avoids race conditions around async
|
||||
`onConfirm` work that should keep the dialog open until the work
|
||||
finishes.
|
||||
- `AlertDialog`'s `onOpenChange(false)` triggers `onCancel` — Escape,
|
||||
the backdrop click (suppressed in AlertDialog so this never fires
|
||||
via that path), and any external `<AlertDialogCancel>` activation
|
||||
all funnel into the same handler.
|
||||
@@ -0,0 +1,63 @@
|
||||
# confirm-delete-dialog.test-fixtures
|
||||
|
||||
## Purpose
|
||||
|
||||
Wave-3g local test fixture that augments the chassis test i18n instance with the
|
||||
`react-ui.confirmDelete.*` keyset. The chassis spec tests under
|
||||
`confirm-delete-dialog.test.tsx` need to assert on the resolved English strings
|
||||
("Delete X?", "Cancel", "Delete", "This action cannot be undone.") before the
|
||||
central wireup commit folds the canonical translations into the bundled
|
||||
`en-GB.json` source-of-truth. Once the wireup commit lands, the bundled keys
|
||||
shadow the fixture and `augmentI18nWithConfirmDeleteKeys` becomes a no-op
|
||||
re-merge — the fixture stays in place during the deletion window so the
|
||||
deletion can land as a single atomic cleanup pass without breaking the suite
|
||||
mid-rebase.
|
||||
|
||||
## Features
|
||||
|
||||
- Bundles localized `confirmDelete` strings for all five chassis-supported
|
||||
locales (`en-GB`, `zh-CN`, `ar-AE`, `cy-GB`, `ru-RU`)
|
||||
- Idempotent merge — calling the augment function twice against the same
|
||||
instance is a no-op
|
||||
- Zero coupling to the fixture's call-site structure: the augment helper accepts
|
||||
any `I18nInstance`, so test setups can compose it freely with other fixtures
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
|
||||
```ts
|
||||
import { createTestI18n } from "../../../tests/setup/test-i18n.js";
|
||||
import { augmentI18nWithConfirmDeleteKeys } from "./confirm-delete-dialog.test-fixtures.js";
|
||||
|
||||
const i18n = await createTestI18n({ locale: "en-GB" });
|
||||
augmentI18nWithConfirmDeleteKeys(i18n);
|
||||
```
|
||||
|
||||
### Advanced Example
|
||||
|
||||
```ts
|
||||
import { createTestI18n } from "../../../tests/setup/test-i18n.js";
|
||||
import { augmentI18nWithConfirmDeleteKeys } from "./confirm-delete-dialog.test-fixtures.js";
|
||||
|
||||
for (const locale of ["en-GB", "ar-AE", "cy-GB"] as const) {
|
||||
const i18n = await createTestI18n({ locale });
|
||||
augmentI18nWithConfirmDeleteKeys(i18n);
|
||||
// Assert against the locale-specific resolved strings.
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `augmentI18nWithConfirmDeleteKeys(i18n: I18nInstance): void`
|
||||
|
||||
Merges the bundled `confirmDelete` keyset into the supplied i18n instance for
|
||||
every supported locale. Returns nothing. Idempotent.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The fixture is intentionally tiny — five locale × four-key bundles plus a loop.
|
||||
Once the wireup commit folds the canonical keys into the chassis `en-GB.json`
|
||||
source-of-truth, this file will be removed alongside its `.md` sibling, the
|
||||
spec test imports rewritten to drop the `augmentI18nWithConfirmDeleteKeys`
|
||||
call, and the change archived as part of the Wave 3g cleanup pass.
|
||||
@@ -0,0 +1,171 @@
|
||||
# ContextMenu
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - context-menu.variants — `contextMenuContentVariants…
|
||||
- [context-menu.variants](./context-menu.variants.md) — `contextMenuContentVariants`, `contextMenuItemVariants`, `contextMenuLabelVariants`, `contextMenuSeparatorVariants`, `c…
|
||||
- context-menu.variants — `contextMenuContentVariants…
|
||||
|
||||
- [context-menu.variants](./context-menu.variants.md) — `contextMenuContentVariants`, `contextMenuItemVariants`, `contextMenuLabelVariants`, `contextMenuSeparatorVariants`, `c…
|
||||
|
||||
- context-menu.variants — `contextMenuContentVariants…
|
||||
|
||||
- [context-menu.variants](./context-menu.variants.md) — `contextMenuContentVariants`, `contextMenuItemVariants`, `contextMenuLabelVariants`, `contextMenuSeparatorVariants`, `c…
|
||||
|
||||
- context-menu.variants — `contextMenuContentVariants…
|
||||
|
||||
- [context-menu.variants](./context-menu.variants.md) — `contextMenuContentVariants`, `contextMenuItemVariants`, `contextMenuLabelVariants`, `contextMenuSeparatorVariants`, `c…
|
||||
- [context-menu.variants](./context-menu.variants.md) — `contextMenuContentVariants`, `contextMenuItemVariants`, `contextMenuLabelVariants`, `contextMenuSeparatorVariants`, `c…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<ContextMenu>` and its sub-components (`<ContextMenuTrigger>`, `<ContextMenuPortal>`, `<ContextMenuContent>`, `<ContextMenuItem>`, `<ContextMenuGroup>`, `<ContextMenuLabel>`, `<ContextMenuSeparator>`, `<ContextMenuShortcut>`, `<ContextMenuCheckboxItem>`, `<ContextMenuRadioGroup>`, `<ContextMenuRadioItem>`, `<ContextMenuSub>`, `<ContextMenuSubTrigger>`, `<ContextMenuSubContent>`) are the chassis right-click / long-press menu primitives built on `@radix-ui/react-context-menu`. Radix supplies the WAI-ARIA menu semantics (`role="menu"`, arrow-key roving focus, `Enter` activates, `Escape` closes, focus-trap while open).
|
||||
|
||||
Per shadcn convention, items typically sit inside a `<ContextMenuGroup>` for grouped items so a11y software announces them as one set (ungrouped items at the top level are also OK). Sub-menus pair `<ContextMenuSubTrigger>` with `<ContextMenuSubContent>` inside a `<ContextMenuSub>`.
|
||||
|
||||
## Features
|
||||
|
||||
- Right-click / long-press to open; arrow-key roving focus; Enter activates; Escape closes.
|
||||
- `<ContextMenuItem>` / `<ContextMenuCheckboxItem>` / `<ContextMenuRadioItem>` for the three item kinds.
|
||||
- Sub-menu support via `<ContextMenuSub>` + `<ContextMenuSubTrigger>` (renders a chevron) + `<ContextMenuSubContent>`.
|
||||
- `<ContextMenuShortcut>` displays a keyboard-shortcut hint with translated `aria-keyshortcuts` attribute via `ariaKeyShortcutsKey: TranslationKey`.
|
||||
- Logical-property gutters (`ps-8`) so checkbox / radio indicators align under both LTR and RTL.
|
||||
- Sub-trigger chevron flips automatically under RTL via `rtl:rotate-180`.
|
||||
- Theme-driven cva for every slot via `useThemedVariants("ContextMenu<Slot>")`.
|
||||
- All sub-components forward refs.
|
||||
- `motion-reduce:animate-none` collapses animations under `prefers-reduced-motion: reduce`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
ContextMenu,
|
||||
ContextMenuContent,
|
||||
ContextMenuItem,
|
||||
ContextMenuTrigger,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function FileItemMenu() {
|
||||
return (
|
||||
<ContextMenu>
|
||||
<ContextMenuTrigger>
|
||||
<div>Right-click on this file</div>
|
||||
</ContextMenuTrigger>
|
||||
<ContextMenuContent>
|
||||
<ContextMenuItem>Open</ContextMenuItem>
|
||||
<ContextMenuItem>Rename</ContextMenuItem>
|
||||
<ContextMenuItem>Delete</ContextMenuItem>
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
ContextMenu,
|
||||
ContextMenuCheckboxItem,
|
||||
ContextMenuContent,
|
||||
ContextMenuGroup,
|
||||
ContextMenuItem,
|
||||
ContextMenuLabel,
|
||||
ContextMenuRadioGroup,
|
||||
ContextMenuRadioItem,
|
||||
ContextMenuSeparator,
|
||||
ContextMenuShortcut,
|
||||
ContextMenuSub,
|
||||
ContextMenuSubContent,
|
||||
ContextMenuSubTrigger,
|
||||
ContextMenuTrigger,
|
||||
} from "@suaveplan/react-ui";
|
||||
import { useState } from "react";
|
||||
|
||||
export function FileExplorerMenu() {
|
||||
const [bookmarks, setBookmarks] = useState(true);
|
||||
const [sortBy, setSortBy] = useState("name");
|
||||
return (
|
||||
<ContextMenu>
|
||||
<ContextMenuTrigger>
|
||||
<div>Right-click for actions</div>
|
||||
</ContextMenuTrigger>
|
||||
<ContextMenuContent>
|
||||
<ContextMenuGroup>
|
||||
<ContextMenuLabel>File</ContextMenuLabel>
|
||||
<ContextMenuItem>
|
||||
Open
|
||||
<ContextMenuShortcut>⌘O</ContextMenuShortcut>
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem>
|
||||
Save
|
||||
<ContextMenuShortcut>⌘S</ContextMenuShortcut>
|
||||
</ContextMenuItem>
|
||||
</ContextMenuGroup>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuCheckboxItem
|
||||
checked={bookmarks}
|
||||
onCheckedChange={setBookmarks}
|
||||
>
|
||||
Show bookmarks
|
||||
</ContextMenuCheckboxItem>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuLabel>Sort by</ContextMenuLabel>
|
||||
<ContextMenuRadioGroup value={sortBy} onValueChange={setSortBy}>
|
||||
<ContextMenuRadioItem value="name">Name</ContextMenuRadioItem>
|
||||
<ContextMenuRadioItem value="date">Date</ContextMenuRadioItem>
|
||||
</ContextMenuRadioGroup>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuSub>
|
||||
<ContextMenuSubTrigger>Share</ContextMenuSubTrigger>
|
||||
<ContextMenuSubContent>
|
||||
<ContextMenuItem>Email</ContextMenuItem>
|
||||
<ContextMenuItem>Copy link</ContextMenuItem>
|
||||
</ContextMenuSubContent>
|
||||
</ContextMenuSub>
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<ContextMenu>`
|
||||
|
||||
Wraps Radix Root. Inherits standard `onOpenChange` / `modal` props.
|
||||
|
||||
### `<ContextMenuItem>`, `<ContextMenuLabel>`, `<ContextMenuSubTrigger>`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `inset` | `boolean` | `false` | Add `ps-8` left gutter to align with checkbox/radio rows. |
|
||||
|
||||
### `<ContextMenuShortcut>`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `ariaKeyShortcutsKey` | `TranslationKey` | _none_ | Branded translation key resolved to the `aria-keyshortcuts` attribute. |
|
||||
| `children` | `ReactNode` | _required_ | The visible shortcut hint (e.g. `⌘S`). |
|
||||
|
||||
### `<ContextMenuCheckboxItem>` / `<ContextMenuRadioItem>`
|
||||
|
||||
Inherit Radix CheckboxItem / RadioItem props (`checked`, `onCheckedChange`, `value`). Render a 32-px gutter for the indicator (CheckIcon / CircleIcon).
|
||||
|
||||
### `<ContextMenuSub>`
|
||||
|
||||
Wraps a sub-menu pair. The `<ContextMenuSubTrigger>` opens the `<ContextMenuSubContent>` when highlighted.
|
||||
|
||||
### `<ContextMenuTrigger>`, `<ContextMenuContent>`, `<ContextMenuGroup>`, `<ContextMenuSeparator>`, `<ContextMenuRadioGroup>`, `<ContextMenuPortal>`, `<ContextMenuSubContent>`
|
||||
|
||||
Direct re-exports of the Radix sub-components with the chassis cva applied.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis NEVER assigns a numeric `z-index` — Radix portals handle stacking.
|
||||
- Logical-property gutters (`ps-8`, `start-2`) so RTL flips the indicator gutter automatically.
|
||||
- `<ContextMenuSubTrigger>` uses `rtl:rotate-180` on the chevron so it visually points "into" the sub-menu in both LTR and RTL.
|
||||
- `<ContextMenuShortcut>` uses `ms-auto` (logical property) so the shortcut is pushed to the trailing edge in both LTR and RTL.
|
||||
- `<ContextMenuContent>` self-portals via `<ContextMenuPortal>` so consumers do not need to compose the portal manually.
|
||||
- Per shadcn convention, items always inside their group (`ContextMenuItem` → `ContextMenuGroup` for grouped items; ungrouped items at the top level are also OK).
|
||||
- `aria-keyshortcuts` on `<ContextMenuShortcut>` is typed as a `TranslationKey` (rather than a free string) so the lint gate (`check-no-hardcoded-strings`) passes — consumers MUST localise keyboard hints.
|
||||
@@ -0,0 +1,68 @@
|
||||
# ContextMenu Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`contextMenuContentVariants`, `contextMenuItemVariants`, `contextMenuLabelVariants`, `contextMenuSeparatorVariants`, `contextMenuShortcutVariants`, `contextMenuCheckboxItemVariants`, `contextMenuRadioItemVariants`, and `contextMenuSubTriggerVariants` are the cva configurations powering every `<ContextMenu>` slot. They are split from `context-menu.tsx` into their own module so an ejected source copy under `npx shadcn@latest add @suaveplan/context-menu` keeps the cva configurations in a dedicated file. Theme overrides flow through `useThemedVariants("ContextMenu<Slot>")`.
|
||||
|
||||
The Item / Label / SubTrigger cva include an `inset` axis so consumers can align rows with checkbox/radio indicator gutters when those item kinds appear in the same menu.
|
||||
|
||||
## Features
|
||||
|
||||
- Content: `min-w-[8rem]` rounded popover panel with open/close animations keyed off Radix `data-state` and `data-side`.
|
||||
- Item: `cursor-default select-none gap-2 rounded-sm px-2 py-1.5`, `focus:bg-accent focus:text-accent-foreground` highlight, `data-[disabled]` decay.
|
||||
- Item.inset → `ps-8` left gutter for alignment with checkbox/radio rows.
|
||||
- Label: `font-semibold` non-interactive heading with optional `inset` axis.
|
||||
- Separator: `-mx-1 my-1 h-px bg-border`.
|
||||
- Shortcut: `ms-auto text-xs tracking-widest text-muted-foreground` (logical-property end-align).
|
||||
- CheckboxItem / RadioItem: 32-px left gutter (`ps-8`) for the indicator (CheckIcon / CircleIcon).
|
||||
- SubTrigger: same shape as Item with `data-[state=open]` highlight; chevron pinned at the end via `ms-auto` and flipped under RTL via `rtl:rotate-180`.
|
||||
- NEVER carries a numeric `z-index`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
contextMenuContentVariants,
|
||||
contextMenuItemVariants,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
const contentClassName = contextMenuContentVariants();
|
||||
const itemClassName = contextMenuItemVariants({ inset: false });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { contextMenuItemVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomMenuItem(props: { destructive: boolean; children: React.ReactNode }) {
|
||||
return (
|
||||
<div
|
||||
role="menuitem"
|
||||
className={cn(
|
||||
contextMenuItemVariants({ inset: true }),
|
||||
props.destructive && "text-destructive focus:bg-destructive focus:text-destructive-foreground"
|
||||
)}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `contextMenuContentVariants(): string`
|
||||
- `contextMenuItemVariants(props?: { inset?: boolean }): string`
|
||||
- `contextMenuLabelVariants(props?: { inset?: boolean }): string`
|
||||
- `contextMenuSeparatorVariants(): string`
|
||||
- `contextMenuShortcutVariants(): string`
|
||||
- `contextMenuCheckboxItemVariants(): string`
|
||||
- `contextMenuRadioItemVariants(): string`
|
||||
- `contextMenuSubTriggerVariants(props?: { inset?: boolean }): string`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- All gutters use logical properties (`ps-8`, `start-2`, `ms-auto`) so RTL flips automatically — `start` becomes `right` and `ms-auto` ends at the left edge under `dir="rtl"`.
|
||||
- The chassis NEVER assigns a numeric `z-index`. Radix's `<Portal>` mounts the content above all page content via DOM placement.
|
||||
- The Sub menu reuses `contextMenuContentVariants()` for its own SubContent so the sub-menu visually mirrors the top-level menu.
|
||||
150
docs/ui/react/react-ui/src/components/data-table/data-table.md
Normal file
150
docs/ui/react/react-ui/src/components/data-table/data-table.md
Normal file
@@ -0,0 +1,150 @@
|
||||
# DataTable
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - data-table.variants — `dataTableVariants`, `dataTab…
|
||||
- [data-table.variants](./data-table.variants.md) — `dataTableVariants`, `dataTablePaginationVariants`, and `dataTableSortHeaderVariants` drive the chassis `<DataTable>` o…
|
||||
- data-table.variants — `dataTableVariants`, `dataTab…
|
||||
|
||||
- [data-table.variants](./data-table.variants.md) — `dataTableVariants`, `dataTablePaginationVariants`, and `dataTableSortHeaderVariants` drive the chassis `<DataTable>` o…
|
||||
|
||||
- data-table.variants — `dataTableVariants`, `dataTab…
|
||||
|
||||
- [data-table.variants](./data-table.variants.md) — `dataTableVariants`, `dataTablePaginationVariants`, and `dataTableSortHeaderVariants` drive the chassis `<DataTable>` o…
|
||||
|
||||
- data-table.variants — `dataTableVariants`, `dataTab…
|
||||
|
||||
- [data-table.variants](./data-table.variants.md) — `dataTableVariants`, `dataTablePaginationVariants`, and `dataTableSortHeaderVariants` drive the chassis `<DataTable>` o…
|
||||
- [data-table.variants](./data-table.variants.md) — `dataTableVariants`, `dataTablePaginationVariants`, and `dataTableSortHeaderVariants` drive the chassis `<DataTable>` o…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<DataTable>` is the chassis headless data-table built on `@tanstack/react-table` (v8) and the chassis `<Table>` family. It is a **suaveplan-only addition** — shadcn does not ship `data-table` as a registry item upstream (per Wave 3a's reconciliation finding #7); shadcn's docs treat data-tables as recipes consumers build on top of the `<Table>` primitive. The chassis exposes `<DataTable>` as a discrete component so consumers do not hand-roll sorting / pagination / selection / empty-state treatment per surface.
|
||||
|
||||
The component is generic over `TData` (the row shape) and `TValue` (the column value union, defaults to `unknown`). Consumers build column definitions with the re-exported `createColumnHelper` from `@tanstack/react-table`. Every chassis-rendered string (no-results, pagination, rows-per-page, selection summary) flows through the active i18n via `t()` calls keyed on bundled `react-ui.dataTable.*` translation keys. The `selected` key uses ICU plural so the count agrees with the active locale's plural rules — Welsh's six-form scale is bundled.
|
||||
|
||||
## Features
|
||||
|
||||
- Built on TanStack Table v8 — the de-facto headless table for React.
|
||||
- Sortable columns — click any sortable column header to toggle asc / desc / none; the header button shows a chevron icon indicating the active sort direction.
|
||||
- Pagination — `pageSize` defaults to 10 rows; previous / next buttons disable at the boundaries.
|
||||
- Row selection (opt-in via `enableRowSelection`) — chassis renders a selection summary using ICU plural translation.
|
||||
- Empty state — when no rows match, the chassis renders the bundled `react-ui.dataTable.noResults` message in a single full-width row.
|
||||
- Stable row keys via `getRowId` (recommended) — without it, TanStack uses array-index keys which collide on re-order.
|
||||
- `data-state="selected"` on selected rows so chassis `<TableRow>` hover / selected styling activates automatically.
|
||||
- Re-exports `createColumnHelper` so consumers don't need to add `@tanstack/react-table` as a direct dep.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { createColumnHelper, DataTable } from "@suaveplan/react-ui";
|
||||
|
||||
interface Invoice {
|
||||
readonly id: string;
|
||||
readonly invoice: string;
|
||||
readonly status: string;
|
||||
readonly amount: number;
|
||||
}
|
||||
|
||||
const columnHelper = createColumnHelper<Invoice>();
|
||||
|
||||
const COLUMNS = [
|
||||
columnHelper.accessor("invoice", { header: "Invoice" }),
|
||||
columnHelper.accessor("status", { header: "Status" }),
|
||||
columnHelper.accessor("amount", {
|
||||
header: "Amount",
|
||||
cell: (info) => `$${info.getValue().toFixed(2)}`,
|
||||
}),
|
||||
];
|
||||
|
||||
export function InvoiceDataTable({ data }: { data: Invoice[] }) {
|
||||
return (
|
||||
<DataTable<Invoice>
|
||||
columns={COLUMNS}
|
||||
data={data}
|
||||
getRowId={(row) => row.id}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { createColumnHelper, DataTable } from "@suaveplan/react-ui";
|
||||
|
||||
interface User {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly email: string;
|
||||
readonly role: "admin" | "member";
|
||||
}
|
||||
|
||||
const columnHelper = createColumnHelper<User>();
|
||||
|
||||
const COLUMNS = [
|
||||
columnHelper.display({
|
||||
id: "select",
|
||||
header: "",
|
||||
cell: ({ row }) => (
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={row.getIsSelected()}
|
||||
onChange={row.getToggleSelectedHandler()}
|
||||
aria-label="select-row"
|
||||
/>
|
||||
),
|
||||
}),
|
||||
columnHelper.accessor("name", { header: "Name", enableSorting: true }),
|
||||
columnHelper.accessor("email", { header: "Email", enableSorting: false }),
|
||||
columnHelper.accessor("role", { header: "Role", enableSorting: true }),
|
||||
];
|
||||
|
||||
export function UserDataTable({ users }: { users: User[] }) {
|
||||
return (
|
||||
<DataTable<User>
|
||||
columns={COLUMNS}
|
||||
data={users}
|
||||
getRowId={(row) => row.id}
|
||||
enableRowSelection
|
||||
pageSize={5}
|
||||
initialSorting={[{ id: "name", desc: false }]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<DataTable<TData, TValue = unknown>>`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `columns` | `ReadonlyArray<ColumnDef<TData, TValue>>` | — | Column definitions; build with `createColumnHelper<TData>()`. |
|
||||
| `data` | `ReadonlyArray<TData>` | — | The rows to render. Must be stable identity-wise (use `useMemo` on the consumer side). |
|
||||
| `getRowId` | `(row: TData, index: number) => string` | _undefined_ | Stable row id for selection / keying. Recommended. |
|
||||
| `enableRowSelection` | `boolean` | `false` | When true, renders a selection summary using ICU plural translation. |
|
||||
| `pageSize` | `number` | `10` | Rows per page. |
|
||||
| `initialSorting` | `SortingState` | `[]` | Initial sort state. |
|
||||
| `noResultsKey` | `TranslationKey` | `react-ui.dataTable.noResults` | Empty-state message. |
|
||||
| `rowsPerPageKey` | `TranslationKey` | `react-ui.dataTable.rowsPerPage` | Pagination footer label. |
|
||||
| `selectedKey` | `TranslationKey` | `react-ui.dataTable.selected` | ICU-plural selection summary; receives `{ count }`. |
|
||||
| `previousKey` | `TranslationKey` | `react-ui.dataTable.previous` | Previous-page button label. |
|
||||
| `nextKey` | `TranslationKey` | `react-ui.dataTable.next` | Next-page button label. |
|
||||
|
||||
### `createColumnHelper<TData>()`
|
||||
|
||||
Re-exported from `@tanstack/react-table`. Returns a builder with `accessor`, `display`, and `group` methods for typed column definitions.
|
||||
|
||||
#### Errors
|
||||
|
||||
The DataTable component calls `useTranslation()` to resolve the chassis-rendered translation keys. It MUST render inside a `<TranslationProvider>` / `<ReactUiProvider>`; otherwise `useTranslation` throws.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The component is generic over `TData` and `TValue` so consumers get full type-safety on column definitions and cell renderers. The cast to `ColumnDef<TData, TValue>[]` inside `useReactTable` is a TanStack ergonomics workaround — TanStack's `data` accepts `TData[]` but its `columns` slot accepts a value-erased shape, so the chassis re-asserts the typed shape at the boundary.
|
||||
- The `selected` plural translation includes the Welsh six-form scale (`zero | one | two | few | many | other`) so the count agrees with the active locale's plural rules. Other locales (`en-GB`, `zh-CN`, `ar-AE`, `ru-RU`) use their respective plural categories from the bundled JSON.
|
||||
- `getRowId` is strongly recommended even when `data` items contain an `id` field — without a stable getter, TanStack uses array index keys, which collide when consumers reorder the data array. The chassis does NOT default `getRowId` to `(row) => row.id` because not every `TData` shape has an `id` field.
|
||||
- The pagination controls are intentionally bare `<button>` elements with `border px-2 py-1` — this keeps the data-table self-contained without forcing a `<Button>` import. Consumers wanting fancier pagination compose their own footer instead of using the built-in one (read pagination state from `table.getState().pagination` and call `table.setPageIndex` / `table.setPageSize` directly).
|
||||
- The sort indicator chevron icon is `aria-hidden="true"` because the column header text already conveys the sort target; screen-readers should not double-announce.
|
||||
- Empty state spans `colSpan={columns.length}` so the no-results message centres across the full table width regardless of column count.
|
||||
@@ -0,0 +1,44 @@
|
||||
# DataTable Variants
|
||||
|
||||
## Purpose
|
||||
|
||||
`dataTableVariants`, `dataTablePaginationVariants`, and `dataTableSortHeaderVariants` drive the chassis `<DataTable>` orchestrator. The root container stacks the rendered `<Table>`, the pagination footer, and (when row selection is enabled) the selection summary in a vertical column. The pagination footer is a flex row with `justify-between` so the selection summary sits on the inline-start and the page controls sit on the inline-end. The sort-header button is an inline-flex with hover and focus states scoped to the button itself, not the surrounding `<th>`.
|
||||
|
||||
## Features
|
||||
|
||||
- Root `dataTableVariants` — `flex w-full flex-col gap-3` so the table, pagination, and selection summary stack with consistent spacing.
|
||||
- `dataTablePaginationVariants` — `flex flex-wrap items-center justify-between gap-2 px-2 text-sm`; `flex-wrap` so the controls stack vertically on narrow viewports.
|
||||
- `dataTableSortHeaderVariants` — inline-flex button with hover (`hover:bg-muted`), focus-ring (`focus:ring-2`), and `motion-reduce:transition-none`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { dataTableSortHeaderVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const sortBtn = dataTableSortHeaderVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { cva } from "class-variance-authority";
|
||||
import { dataTableVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const denseDataTable = cva(
|
||||
[dataTableVariants(), "[&_td]:py-1"].join(" ")
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
Each cva resolver returns a static class string — no options. Module exports:
|
||||
|
||||
- `dataTableVariants()` — root wrapper
|
||||
- `dataTablePaginationVariants()` — pagination footer flex row
|
||||
- `dataTableSortHeaderVariants()` — sortable column header button
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis pagination controls are intentionally bare-bones (`<button>` with `border px-2 py-1`) — this keeps the data-table self-contained without forcing a `<Button>` import. Consumers wanting the chassis Button styling override at the call site by composing their own pagination footer instead of using the built-in one.
|
||||
- The sort-header button uses `-ms-1` so its hover background extends slightly to the inline-start, visually balancing the `<th>` cell's `px-2` padding without offsetting the label text alignment.
|
||||
- `flex-wrap` on the pagination row ensures controls stack vertically when the viewport is too narrow for the inline layout — important for narrow surfaces (drawers, sidebars).
|
||||
181
docs/ui/react/react-ui/src/components/dialog/dialog.md
Normal file
181
docs/ui/react/react-ui/src/components/dialog/dialog.md
Normal file
@@ -0,0 +1,181 @@
|
||||
# Dialog
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - dialog.variants — `dialogOverlayVariants`, `dialogC…
|
||||
- [dialog.variants](./dialog.variants.md) — `dialogOverlayVariants`, `dialogContentVariants`, `dialogHeaderVariants`, `dialogFooterVariants`, `dialogTitleVariants`…
|
||||
- dialog.variants — `dialogOverlayVariants`, `dialogC…
|
||||
|
||||
- [dialog.variants](./dialog.variants.md) — `dialogOverlayVariants`, `dialogContentVariants`, `dialogHeaderVariants`, `dialogFooterVariants`, `dialogTitleVariants`…
|
||||
|
||||
- dialog.variants — `dialogOverlayVariants`, `dialogC…
|
||||
|
||||
- [dialog.variants](./dialog.variants.md) — `dialogOverlayVariants`, `dialogContentVariants`, `dialogHeaderVariants`, `dialogFooterVariants`, `dialogTitleVariants`…
|
||||
|
||||
- dialog.variants — `dialogOverlayVariants`, `dialogC…
|
||||
|
||||
- [dialog.variants](./dialog.variants.md) — `dialogOverlayVariants`, `dialogContentVariants`, `dialogHeaderVariants`, `dialogFooterVariants`, `dialogTitleVariants`…
|
||||
- [dialog.variants](./dialog.variants.md) — `dialogOverlayVariants`, `dialogContentVariants`, `dialogHeaderVariants`, `dialogFooterVariants`, `dialogTitleVariants`…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Dialog>` and its sub-components (`<DialogTrigger>`, `<DialogPortal>`, `<DialogOverlay>`, `<DialogContent>`, `<DialogHeader>`, `<DialogFooter>`, `<DialogTitle>`, `<DialogDescription>`, `<DialogClose>`) are the chassis modal dialog primitives built on `@radix-ui/react-dialog`. Radix supplies the WAI-ARIA dialog semantics (`role="dialog"`, `aria-modal="true"`, focus-trap on open, restore-focus to the trigger on close, Escape closes, click-outside backdrop dismissal) and the chassis layers a `size` axis on `<DialogContent>` (`sm` / `default` / `lg` / `xl`), a translated lucide-icon close button pinned to the top-end corner, and dedicated cva slots for the header / footer / title / description.
|
||||
|
||||
Per the WAI-ARIA dialog pattern, every dialog MUST contain a `<DialogTitle>` so the accessible name is announced. If the visible UI does not need a heading, render the title with `className="sr-only"` so screen-readers still pick it up while the visible layout stays clean.
|
||||
|
||||
## Features
|
||||
|
||||
- Focus-trap on open; restore-focus to the original trigger on close (Radix-managed).
|
||||
- Escape closes the dialog (Radix-managed); click on the overlay backdrop closes the dialog (Radix-managed).
|
||||
- Translated close-button `aria-label` driven by the bundled `react-ui.dialog.close` key — fallback per locale (Close / 关闭 / إغلاق / Cau / Закрыть). Override per-instance with `closeAriaLabelKey`.
|
||||
- Four content sizes (`sm`, `default`, `lg`, `xl`) scale the panel max-width.
|
||||
- Theme-driven cva overrides for every slot via `useThemedVariants("DialogOverlay" | "DialogContent" | "DialogHeader" | "DialogFooter" | "DialogTitle" | "DialogDescription")`.
|
||||
- All sub-components forward refs to their underlying Radix elements.
|
||||
- `motion-reduce:animate-none` collapses the open/close animations under `prefers-reduced-motion: reduce`.
|
||||
- Logical-property positioning (`end-4`) so the close button sits on the right in LTR and the left in RTL.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function ConfirmDialog() {
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger>Open dialog</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Are you sure?</DialogTitle>
|
||||
<DialogDescription>
|
||||
This action cannot be undone.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<DialogClose>Cancel</DialogClose>
|
||||
<DialogClose>Confirm</DialogClose>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@suaveplan/react-ui";
|
||||
import { useState } from "react";
|
||||
|
||||
export function DeleteAccountDialog({ onConfirm }: { onConfirm(): Promise<void> }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
async function handleConfirm() {
|
||||
setBusy(true);
|
||||
try {
|
||||
await onConfirm();
|
||||
setOpen(false);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="destructive">Delete account</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent size="sm">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Delete account</DialogTitle>
|
||||
<DialogDescription>
|
||||
This will permanently erase all of your data and cannot
|
||||
be undone.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setOpen(false)}
|
||||
disabled={busy}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={handleConfirm}
|
||||
disabled={busy}
|
||||
>
|
||||
{busy ? "Deleting…" : "Delete"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Dialog>`
|
||||
|
||||
Renders the Radix Root that owns the open/close state machine.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `open` | `boolean` | _uncontrolled_ | Controlled open state. |
|
||||
| `defaultOpen` | `boolean` | `false` | Initial open state for uncontrolled use. |
|
||||
| `onOpenChange` | `(open: boolean) => void` | _none_ | Fires on open/close transitions. |
|
||||
| `modal` | `boolean` | `true` | When false, focus is not trapped (rare; modal recommended). |
|
||||
|
||||
### `<DialogTrigger>`
|
||||
|
||||
Wraps the Radix Trigger; renders a `<button>` (or polymorphically the child via `asChild`).
|
||||
|
||||
### `<DialogContent>`
|
||||
|
||||
Wraps the Radix Content (rendered into a portal under `<DialogPortal>`).
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `size` | `"sm" \| "default" \| "lg" \| "xl"` | `"default"` | Visual variant scaling the panel max-width. |
|
||||
| `closeAriaLabelKey` | `TranslationKey` | `react-ui.dialog.close` | Override the close-button accessible name. |
|
||||
|
||||
### `<DialogHeader>`, `<DialogFooter>`
|
||||
|
||||
Pure layout containers — flex column / flex row with end-justify on larger screens.
|
||||
|
||||
### `<DialogTitle>`
|
||||
|
||||
REQUIRED — Radix surfaces a dev warning when omitted. Render `className="sr-only"` if the visible layout does not need a heading.
|
||||
|
||||
### `<DialogDescription>`, `<DialogClose>`, `<DialogPortal>`, `<DialogOverlay>`
|
||||
|
||||
Direct re-exports of the Radix sub-components with the chassis cva applied.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis NEVER assigns a numeric `z-index` to the overlay or content panels — Radix handles stacking via portal placement (per the shadcn skill rule).
|
||||
- The close button sits at `end-4` (logical property) so it sits on the right under LTR and the left under RTL automatically.
|
||||
- The `aria-label` on the close button flows through `useTranslation()` so consumers can swap the key per call (`closeAriaLabelKey="…"`) without touching the component.
|
||||
- Radix's a11y title check is a dev-only `console.warn`; tests assert presence by querying for the rendered `<h2>`.
|
||||
- The animation classes (`data-[state=open]:animate-in` etc.) require the consumer's Tailwind setup to load `tailwindcss-animate` — the chassis ships this via `@suaveplan/react-ui/styles/global.css`.
|
||||
- The dialog content is wrapped in its own `<DialogPortal>` inside `<DialogContent>` so consumers do not need to remember to portal manually — this matches the shadcn upstream convention exactly.
|
||||
@@ -0,0 +1,71 @@
|
||||
# Dialog Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`dialogOverlayVariants`, `dialogContentVariants`, `dialogHeaderVariants`, `dialogFooterVariants`, `dialogTitleVariants`, `dialogDescriptionVariants`, and `dialogCloseButtonVariants` are the cva (class-variance-authority) configurations powering every `<Dialog>` slot. They are split from `dialog.tsx` into their own module so an ejected source copy under `npx shadcn@latest add @suaveplan/dialog` keeps the cva configurations in a dedicated file. Theme overrides flow through `useThemedVariants("Dialog<Slot>")`.
|
||||
|
||||
The overlay and content cva carry the open/close animation classes; the structural slots (header/footer/title/description) carry layout-only classes so consumers can compose visually rich variations without escaping the cva surface.
|
||||
|
||||
## Features
|
||||
|
||||
- Overlay: `fixed inset-0 bg-black/80` with `data-[state=open]:animate-in data-[state=closed]:animate-out` keyed off Radix's `data-state` attribute.
|
||||
- Content: centred panel via `translate-x-[-50%] translate-y-[-50%]`, four sizes (`sm` → `max-w-sm`, `default` → `max-w-lg`, `lg` → `max-w-2xl`, `xl` → `max-w-4xl`), shadow, sm-rounded.
|
||||
- Header: flex column, `text-center sm:text-start` so RTL locales align correctly on small screens.
|
||||
- Footer: flex column-reverse stacking on small screens, flex row with end-justify above `sm`.
|
||||
- Title: large semibold heading; tracking tightened.
|
||||
- Description: muted-foreground text-sm.
|
||||
- Close button: pinned at `end-4 top-4` (logical property — flips automatically in RTL); `focus:ring-2`, `disabled:pointer-events-none`, `motion-reduce:transition-none`.
|
||||
- NEVER carries a numeric `z-index` (Radix portals handle stacking).
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
dialogContentVariants,
|
||||
dialogTitleVariants,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
const contentClassName = dialogContentVariants({ size: "lg" });
|
||||
const titleClassName = dialogTitleVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { dialogContentVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomPanel() {
|
||||
return (
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
className={cn(
|
||||
dialogContentVariants({ size: "xl" }),
|
||||
"border-2 border-primary"
|
||||
)}
|
||||
>
|
||||
…
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `dialogOverlayVariants(): string`
|
||||
|
||||
Returns the overlay backdrop classes; takes no arguments.
|
||||
|
||||
### `dialogContentVariants(props?: { size?: "sm" | "default" | "lg" | "xl" }): string`
|
||||
|
||||
Returns the panel classes. Omitted size falls back to `defaultVariants.size = "default"`.
|
||||
|
||||
### `dialogHeaderVariants(): string` / `dialogFooterVariants(): string` / `dialogTitleVariants(): string` / `dialogDescriptionVariants(): string` / `dialogCloseButtonVariants(): string`
|
||||
|
||||
Each returns a single class string suitable for use inside `cn(...)` chains. None take arguments — the layout slots are not parameterised by design.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The cva uses `motion-reduce:animate-none` on the overlay AND content so the entire open/close transition collapses for `prefers-reduced-motion: reduce`. Consumers can layer their own additional motion-reduce overrides via `cn(...)` if they want a more elaborate fall-back.
|
||||
- Per the shadcn skill rule, the cva NEVER assigns a numeric `z-index`. Radix's `<Portal>` mounts the overlay + content into the document body so stacking is implicit.
|
||||
- The close-button cva uses logical properties (`end-4`) rather than `right-4` so RTL flips for free.
|
||||
149
docs/ui/react/react-ui/src/components/drawer/drawer.md
Normal file
149
docs/ui/react/react-ui/src/components/drawer/drawer.md
Normal file
@@ -0,0 +1,149 @@
|
||||
# Drawer
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - drawer.variants — `drawerOverlayVariants`, `drawerC…
|
||||
- [drawer.variants](./drawer.variants.md) — `drawerOverlayVariants`, `drawerContentVariants`, `drawerHandleVariants`, `drawerHeaderVariants`, `drawerFooterVariants…
|
||||
- drawer.variants — `drawerOverlayVariants`, `drawerC…
|
||||
|
||||
- [drawer.variants](./drawer.variants.md) — `drawerOverlayVariants`, `drawerContentVariants`, `drawerHandleVariants`, `drawerHeaderVariants`, `drawerFooterVariants…
|
||||
|
||||
- drawer.variants — `drawerOverlayVariants`, `drawerC…
|
||||
|
||||
- [drawer.variants](./drawer.variants.md) — `drawerOverlayVariants`, `drawerContentVariants`, `drawerHandleVariants`, `drawerHeaderVariants`, `drawerFooterVariants…
|
||||
|
||||
- drawer.variants — `drawerOverlayVariants`, `drawerC…
|
||||
|
||||
- [drawer.variants](./drawer.variants.md) — `drawerOverlayVariants`, `drawerContentVariants`, `drawerHandleVariants`, `drawerHeaderVariants`, `drawerFooterVariants…
|
||||
- [drawer.variants](./drawer.variants.md) — `drawerOverlayVariants`, `drawerContentVariants`, `drawerHandleVariants`, `drawerHeaderVariants`, `drawerFooterVariants…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Drawer>` and its sub-components (`<DrawerTrigger>`, `<DrawerPortal>`, `<DrawerOverlay>`, `<DrawerContent>`, `<DrawerHandle>`, `<DrawerHeader>`, `<DrawerFooter>`, `<DrawerTitle>`, `<DrawerDescription>`, `<DrawerClose>`) are the chassis bottom-anchored modal panel primitives built on `vaul`. Vaul wraps `@radix-ui/react-dialog` internally so the WAI-ARIA dialog semantics (focus-trap, restore-focus, Escape closes, click-outside closes) are inherited; vaul layers on the swipe-down dismiss gesture, snap-points, and background-scale animation that distinguish a Drawer from a Sheet.
|
||||
|
||||
Per the WAI-ARIA dialog pattern, every drawer MUST contain a `<DrawerTitle>` so the accessible name is announced. Use `className="sr-only"` if the visible UI does not need a heading. Drawer is mobile-first — the panel slides up from the bottom and is grabbable by the small handle at the top.
|
||||
|
||||
## Features
|
||||
|
||||
- Bottom-anchored slide-up panel with swipe-down dismiss gesture (vaul-native).
|
||||
- Snap-points support via vaul (`snapPoints={[0.4, 0.8]}`).
|
||||
- Background-scale animation while the drawer is open (`shouldScaleBackground`, default `true`).
|
||||
- Drag handle at the top of the panel (rendered automatically inside `<DrawerContent>`).
|
||||
- Focus-trap on open; restore-focus on close (vaul → Radix-managed).
|
||||
- Escape closes the drawer; click on the overlay backdrop closes the drawer.
|
||||
- All sub-components forward refs.
|
||||
- Theme-driven cva overrides for every slot via `useThemedVariants("Drawer<Slot>")`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Drawer,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerDescription,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
DrawerTrigger,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function AccountDrawer() {
|
||||
return (
|
||||
<Drawer>
|
||||
<DrawerTrigger>Open settings</DrawerTrigger>
|
||||
<DrawerContent>
|
||||
<DrawerHeader>
|
||||
<DrawerTitle>Account settings</DrawerTitle>
|
||||
<DrawerDescription>
|
||||
Manage your account preferences.
|
||||
</DrawerDescription>
|
||||
</DrawerHeader>
|
||||
<DrawerFooter>
|
||||
<DrawerClose>Close</DrawerClose>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Button,
|
||||
Drawer,
|
||||
DrawerContent,
|
||||
DrawerDescription,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
} from "@suaveplan/react-ui";
|
||||
import { useState } from "react";
|
||||
|
||||
export function SnappingFiltersDrawer() {
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<Drawer
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
snapPoints={[0.4, 0.7, 1.0]}
|
||||
shouldScaleBackground
|
||||
>
|
||||
<Button onClick={() => setOpen(true)}>Open filters</Button>
|
||||
<DrawerContent>
|
||||
<DrawerHeader>
|
||||
<DrawerTitle>Filters</DrawerTitle>
|
||||
<DrawerDescription>
|
||||
Drag the handle to expand.
|
||||
</DrawerDescription>
|
||||
</DrawerHeader>
|
||||
<div className="p-4">…filter controls…</div>
|
||||
<DrawerFooter>
|
||||
<Button variant="outline" onClick={() => setOpen(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={() => setOpen(false)}>Apply</Button>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Drawer>`
|
||||
|
||||
Wraps vaul's `Drawer.Root`. Inherits every prop from vaul including:
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `open` | `boolean` | _uncontrolled_ | Controlled open state. |
|
||||
| `defaultOpen` | `boolean` | `false` | Initial open state. |
|
||||
| `onOpenChange` | `(open: boolean) => void` | _none_ | Fires on open/close transitions. |
|
||||
| `snapPoints` | `(number \| string)[]` | _none_ | Optional snap-point heights. |
|
||||
| `shouldScaleBackground` | `boolean` | `true` | Scale the page behind the drawer while open. |
|
||||
| `dismissible` | `boolean` | `true` | Allow Escape / click-outside / drag-down dismissal. |
|
||||
| `modal` | `boolean` | `true` | Keep focus trapped inside the drawer. |
|
||||
|
||||
### `<DrawerContent>`
|
||||
|
||||
Renders the bottom-anchored panel. Self-portals via `<DrawerPortal>` and renders a drag handle at the top.
|
||||
|
||||
### `<DrawerTitle>`
|
||||
|
||||
REQUIRED — vaul/Radix surfaces a dev warning when omitted. Render `className="sr-only"` if no visible heading is needed.
|
||||
|
||||
### `<DrawerTrigger>`, `<DrawerPortal>`, `<DrawerOverlay>`, `<DrawerHandle>`, `<DrawerHeader>`, `<DrawerFooter>`, `<DrawerDescription>`, `<DrawerClose>`
|
||||
|
||||
Direct re-exports of the vaul sub-components with the chassis cva applied.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis NEVER assigns a numeric `z-index` to the overlay or content panels — vaul/Radix portals handle stacking.
|
||||
- `<DrawerContent>` self-portals via `<DrawerPortal>` and renders a `<DrawerHandle>` at the top automatically — consumers do not need to compose them manually. This matches the shadcn `drawer` upstream recipe.
|
||||
- Drag-down dismissal is a vaul-native gesture that requires real pointer events; happy-dom-based unit tests cover the open/close lifecycle via Escape and `onOpenChange` rather than the drag gesture itself. End-to-end coverage of the swipe gesture lives in the consumer's Playwright suite.
|
||||
- `shouldScaleBackground` defaults to `true` (chassis-overrides vaul's `false`) because the scale-down effect is the primary visual cue distinguishing a Drawer from a Sheet.
|
||||
- Vaul's `DrawerClose` button forwards to `onOpenChange(false)` rather than mutating internal state directly — controlled consumers should listen on `onOpenChange` and update their local `open` state to honour the close request.
|
||||
@@ -0,0 +1,64 @@
|
||||
# Drawer Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`drawerOverlayVariants`, `drawerContentVariants`, `drawerHandleVariants`, `drawerHeaderVariants`, `drawerFooterVariants`, `drawerTitleVariants`, and `drawerDescriptionVariants` are the cva configurations powering every `<Drawer>` slot. They are split from `drawer.tsx` into their own module so an ejected source copy under `npx shadcn@latest add @suaveplan/drawer` keeps the cva configurations in a dedicated file. Theme overrides flow through `useThemedVariants("Drawer<Slot>")`.
|
||||
|
||||
All slots are layout-only — the chassis cva does not parameterise the drawer's behaviour (vaul handles snap-points / direction / background-scale at the runtime level), only its visual recipe.
|
||||
|
||||
## Features
|
||||
|
||||
- Overlay: `fixed inset-0 bg-black/80` matching Sheet/Dialog visual language.
|
||||
- Content: `fixed inset-x-0 bottom-0` anchored to the bottom of the viewport with rounded top, border, and bg-background fill.
|
||||
- Handle: small `bg-muted` pill rendered above the panel content as a drag affordance.
|
||||
- Header / footer / title / description: layout-only utilities matching the shadcn upstream `drawer` recipe.
|
||||
- NEVER carries a numeric `z-index` (vaul/Radix Portal handle stacking).
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { drawerContentVariants, drawerTitleVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const contentClassName = drawerContentVariants();
|
||||
const titleClassName = drawerTitleVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { drawerContentVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomBottomSheet() {
|
||||
return (
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
className={cn(
|
||||
drawerContentVariants(),
|
||||
"border-t-2 border-primary"
|
||||
)}
|
||||
>
|
||||
…
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
Every cva resolver returns a single class string suitable for use inside `cn(...)` chains. None take arguments — the drawer slots are not parameterised by design.
|
||||
|
||||
- `drawerOverlayVariants(): string`
|
||||
- `drawerContentVariants(): string`
|
||||
- `drawerHandleVariants(): string`
|
||||
- `drawerHeaderVariants(): string`
|
||||
- `drawerFooterVariants(): string`
|
||||
- `drawerTitleVariants(): string`
|
||||
- `drawerDescriptionVariants(): string`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Per the shadcn skill rule, the cva NEVER assigns a numeric `z-index`. Vaul wraps Radix's `<Portal>` to host the content above all page content via DOM placement.
|
||||
- The chassis paints the drawer's visual recipe; vaul provides the slide animation, snap-point physics, and swipe-down-dismiss gesture at runtime.
|
||||
- The handle slot uses `mx-auto` so it stays centred regardless of the drawer's width — RTL-safe by construction.
|
||||
- Padding (`p-4`) on header / footer is applied at the slot level so consumers can override per-instance via `cn(...)` without touching the cva.
|
||||
@@ -0,0 +1,192 @@
|
||||
# DropdownMenu
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - dropdown-menu.variants — `dropdownMenuContentVarian…
|
||||
- [dropdown-menu.variants](./dropdown-menu.variants.md) — `dropdownMenuContentVariants`, `dropdownMenuItemVariants`, `dropdownMenuLabelVariants`, `dropdownMenuSeparatorVariants`…
|
||||
- dropdown-menu.variants — `dropdownMenuContentVarian…
|
||||
|
||||
- [dropdown-menu.variants](./dropdown-menu.variants.md) — `dropdownMenuContentVariants`, `dropdownMenuItemVariants`, `dropdownMenuLabelVariants`, `dropdownMenuSeparatorVariants`…
|
||||
|
||||
- dropdown-menu.variants — `dropdownMenuContentVarian…
|
||||
|
||||
- [dropdown-menu.variants](./dropdown-menu.variants.md) — `dropdownMenuContentVariants`, `dropdownMenuItemVariants`, `dropdownMenuLabelVariants`, `dropdownMenuSeparatorVariants`…
|
||||
|
||||
- dropdown-menu.variants — `dropdownMenuContentVarian…
|
||||
|
||||
- [dropdown-menu.variants](./dropdown-menu.variants.md) — `dropdownMenuContentVariants`, `dropdownMenuItemVariants`, `dropdownMenuLabelVariants`, `dropdownMenuSeparatorVariants`…
|
||||
- [dropdown-menu.variants](./dropdown-menu.variants.md) — `dropdownMenuContentVariants`, `dropdownMenuItemVariants`, `dropdownMenuLabelVariants`, `dropdownMenuSeparatorVariants`…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<DropdownMenu>` and its sub-components (`<DropdownMenuTrigger>`, `<DropdownMenuContent>`, `<DropdownMenuItem>`, `<DropdownMenuGroup>`, `<DropdownMenuLabel>`, `<DropdownMenuSeparator>`, `<DropdownMenuShortcut>`, `<DropdownMenuCheckboxItem>`, `<DropdownMenuRadioGroup>`, `<DropdownMenuRadioItem>`, `<DropdownMenuSub>`, `<DropdownMenuSubTrigger>`, `<DropdownMenuSubContent>`, `<DropdownMenuPortal>`) are the chassis dropdown-menu primitive built on `@radix-ui/react-dropdown-menu`. Radix supplies the WAI-ARIA menu semantics (`role="menu"`, `role="menuitem"`, arrow-key roving focus, type-ahead search, Escape closes, focus-trap while open, restore-focus to the trigger on close), and the chassis layers cva slots, theme overrides, and a translated `<DropdownMenuShortcut>` `aria-keyshortcuts` attribute on top.
|
||||
|
||||
**Composition rule (shadcn skill rule):** items typically sit inside a `<DropdownMenuGroup>` so screen-readers announce them as one set. Single ungrouped items at the top level are also acceptable. Sub-menus pair `<DropdownMenuSubTrigger>` with `<DropdownMenuSubContent>` inside a `<DropdownMenuSub>`.
|
||||
|
||||
## Features
|
||||
|
||||
- WAI-ARIA menu semantics from Radix.
|
||||
- Arrow-key roving focus + type-ahead search.
|
||||
- Escape closes; click-outside dismisses; restore-focus to the trigger on close.
|
||||
- Auto-portalled `<DropdownMenuContent>` (matches shadcn upstream — consumers do not need to remember `<DropdownMenuPortal>`).
|
||||
- `<DropdownMenuShortcut>` accepts an `ariaKeyShortcutsKey` translation key so the localised shortcut announcement reaches assistive tech.
|
||||
- `<DropdownMenuCheckboxItem>` / `<DropdownMenuRadioGroup>` / `<DropdownMenuRadioItem>` with 32-px gutter for the indicator.
|
||||
- `<DropdownMenuSub>` for nested sub-menus with a chevron indicator (RTL-flipped via `rtl:rotate-180`).
|
||||
- `inset` variant on `<DropdownMenuItem>` / `<DropdownMenuLabel>` / `<DropdownMenuSubTrigger>` aligns with checkbox/radio rows.
|
||||
- All sub-components forward refs to their underlying Radix elements.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function UserMenu() {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger>Open menu</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem>Profile</DropdownMenuItem>
|
||||
<DropdownMenuItem>Settings</DropdownMenuItem>
|
||||
<DropdownMenuItem>Sign out</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Button,
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuTrigger,
|
||||
} from "@suaveplan/react-ui";
|
||||
import { useState } from "react";
|
||||
|
||||
export function EditorMenu() {
|
||||
const [showLineNumbers, setShowLineNumbers] = useState(true);
|
||||
const [theme, setTheme] = useState<"light" | "dark">("light");
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline">View</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuLabel>Editor</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem>
|
||||
Save
|
||||
<DropdownMenuShortcut ariaKeyShortcutsKey="app.shortcuts.save">
|
||||
⌘S
|
||||
</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuCheckboxItem
|
||||
checked={showLineNumbers}
|
||||
onCheckedChange={(checked) =>
|
||||
setShowLineNumbers(Boolean(checked))
|
||||
}
|
||||
>
|
||||
Line numbers
|
||||
</DropdownMenuCheckboxItem>
|
||||
<DropdownMenuRadioGroup
|
||||
value={theme}
|
||||
onValueChange={(v) => setTheme(v as "light" | "dark")}
|
||||
>
|
||||
<DropdownMenuRadioItem value="light">
|
||||
Light theme
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="dark">
|
||||
Dark theme
|
||||
</DropdownMenuRadioItem>
|
||||
</DropdownMenuRadioGroup>
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger>Export</DropdownMenuSubTrigger>
|
||||
<DropdownMenuSubContent>
|
||||
<DropdownMenuItem>PDF</DropdownMenuItem>
|
||||
<DropdownMenuItem>HTML</DropdownMenuItem>
|
||||
<DropdownMenuItem>Markdown</DropdownMenuItem>
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuSub>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<DropdownMenu>`
|
||||
|
||||
The Radix Root. Standard Radix Root props (`open`, `defaultOpen`, `onOpenChange`, `modal`, `dir`).
|
||||
|
||||
### `<DropdownMenuTrigger>`
|
||||
|
||||
Wraps the Radix Trigger (renders a `<button>` or polymorphically the child via `asChild`).
|
||||
|
||||
### `<DropdownMenuContent>`
|
||||
|
||||
The floating menu surface. Auto-portalled. Accepts standard Radix Content props (`side`, `align`, `sideOffset`, `alignOffset`, `collisionPadding`, etc.).
|
||||
|
||||
### `<DropdownMenuItem>`
|
||||
|
||||
A single menu item.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `inset` | `boolean` | `false` | Adds 32-px left gutter. |
|
||||
| `disabled` | `boolean` | `false` | Skips in keyboard nav and ignores activation. |
|
||||
| `onSelect` | `(event: Event) => void` | _none_ | Fires on activation. |
|
||||
|
||||
### `<DropdownMenuGroup>`, `<DropdownMenuLabel>`, `<DropdownMenuSeparator>`
|
||||
|
||||
Standard menu structure. `<DropdownMenuLabel>` accepts an `inset` prop for alignment.
|
||||
|
||||
### `<DropdownMenuShortcut>`
|
||||
|
||||
Keyboard-shortcut hint pinned at the end of an item.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `ariaKeyShortcutsKey` | `TranslationKey` | _none_ | When present, sets `aria-keyshortcuts` to the translated shortcut. |
|
||||
|
||||
### `<DropdownMenuCheckboxItem>`, `<DropdownMenuRadioGroup>`, `<DropdownMenuRadioItem>`
|
||||
|
||||
Standard Radix props (`checked`, `onCheckedChange`, `value`, `onValueChange`).
|
||||
|
||||
### `<DropdownMenuSub>`, `<DropdownMenuSubTrigger>`, `<DropdownMenuSubContent>`
|
||||
|
||||
Nested sub-menu. `<DropdownMenuSubTrigger>` accepts an `inset` prop and renders a chevron icon (RTL-flipped via `rtl:rotate-180`).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis NEVER assigns a numeric `z-index` to the menu — Radix portals handle stacking.
|
||||
- The check / dot indicators sit at `start-2` (logical property) so they flip correctly in RTL contexts.
|
||||
- `motion-reduce:animate-none` collapses the open/close transition for `prefers-reduced-motion: reduce`.
|
||||
- The `<DropdownMenuShortcut>` separates the visible text (passed as children) from the localised announcement (`ariaKeyShortcutsKey`) so a glyph like `⌘S` can be shown to sighted users while screen-readers hear "Command S" or the localised equivalent.
|
||||
- Per the shadcn skill rule, items in groups MUST live inside `<DropdownMenuGroup>` for correct screen-reader announcement.
|
||||
- `<DropdownMenuContent>` auto-portals via `<DropdownMenuPortal>` so consumers do not have to remember to wrap manually — matches shadcn upstream's convention.
|
||||
@@ -0,0 +1,96 @@
|
||||
# DropdownMenu Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`dropdownMenuContentVariants`, `dropdownMenuItemVariants`, `dropdownMenuLabelVariants`, `dropdownMenuSeparatorVariants`, `dropdownMenuShortcutVariants`, `dropdownMenuCheckboxItemVariants`, `dropdownMenuRadioItemVariants`, and `dropdownMenuSubTriggerVariants` are the cva (class-variance-authority) configurations powering every `<DropdownMenu>` slot. They are split from `dropdown-menu.tsx` into their own module so an ejected source copy under `npx shadcn@latest add @suaveplan/dropdown-menu` keeps the cva configurations in a dedicated file. Theme overrides flow through `useThemedVariants("DropdownMenu<Slot>")`.
|
||||
|
||||
The Item / Label / SubTrigger cvas all carry an `inset` axis so consumers can align rows whether or not they include checkbox / radio indicators. The content cva carries the open/close animations keyed off Radix's `data-state` and `data-side` attributes.
|
||||
|
||||
## Features
|
||||
|
||||
- Content: `min-w-[8rem]` floating surface with side-aware slide-in animations and `motion-reduce:animate-none`.
|
||||
- Item: focus-bg lift via `focus:bg-accent`, disabled opacity, optional `ps-8` inset gutter.
|
||||
- Label: semibold heading text, optional inset gutter.
|
||||
- Separator: `-mx-1 my-1 h-px bg-border` divider.
|
||||
- Shortcut: `ms-auto text-xs tracking-widest text-muted-foreground` row terminator.
|
||||
- Checkbox / Radio item: `ps-8 pe-2` to leave space for the start-aligned indicator.
|
||||
- SubTrigger: open-state lifts background to `data-[state=open]:bg-accent`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
dropdownMenuContentVariants,
|
||||
dropdownMenuItemVariants,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
const contentClassName = dropdownMenuContentVariants();
|
||||
const itemClassName = dropdownMenuItemVariants({ inset: true });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { dropdownMenuContentVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function WideContent({
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
dropdownMenuContentVariants(),
|
||||
"min-w-[16rem]",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `dropdownMenuContentVariants(): string`
|
||||
|
||||
Returns the floating surface class string. No arguments.
|
||||
|
||||
### `dropdownMenuItemVariants(props?: { inset?: boolean }): string`
|
||||
|
||||
Returns the item class string. Defaults to `{ inset: false }`.
|
||||
|
||||
### `dropdownMenuLabelVariants(props?: { inset?: boolean }): string`
|
||||
|
||||
Returns the label class string. Defaults to `{ inset: false }`.
|
||||
|
||||
### `dropdownMenuSeparatorVariants(): string`
|
||||
|
||||
Returns the separator class string. No arguments.
|
||||
|
||||
### `dropdownMenuShortcutVariants(): string`
|
||||
|
||||
Returns the shortcut class string. No arguments.
|
||||
|
||||
### `dropdownMenuCheckboxItemVariants(): string`
|
||||
|
||||
Returns the checkbox item class string. No arguments.
|
||||
|
||||
### `dropdownMenuRadioItemVariants(): string`
|
||||
|
||||
Returns the radio item class string. No arguments.
|
||||
|
||||
### `dropdownMenuSubTriggerVariants(props?: { inset?: boolean }): string`
|
||||
|
||||
Returns the sub-trigger class string. Defaults to `{ inset: false }`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Per the shadcn skill rule, no numeric `z-index` is assigned anywhere — Radix portals handle stacking.
|
||||
- The check / dot indicators sit at `start-2` (logical property) so they flip correctly in RTL.
|
||||
- `motion-reduce:animate-none` collapses the open/close animation for `prefers-reduced-motion: reduce`.
|
||||
- The Sub-trigger chevron carries `ms-auto` so the icon flips to the leading edge in RTL via the logical-margin property.
|
||||
141
docs/ui/react/react-ui/src/components/empty/empty.md
Normal file
141
docs/ui/react/react-ui/src/components/empty/empty.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# Empty
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - empty.variants — `emptyVariants` is the cva config……
|
||||
- [empty.variants](./empty.variants.md) — `emptyVariants` is the cva config that powers the `<Empty>` chassis component (with `emptyHeaderVariants`, `emptyMediaV…
|
||||
- empty.variants — `emptyVariants` is the cva config……
|
||||
|
||||
- [empty.variants](./empty.variants.md) — `emptyVariants` is the cva config that powers the `<Empty>` chassis component (with `emptyHeaderVariants`, `emptyMediaV…
|
||||
|
||||
- empty.variants — `emptyVariants` is the cva config……
|
||||
|
||||
- [empty.variants](./empty.variants.md) — `emptyVariants` is the cva config that powers the `<Empty>` chassis component (with `emptyHeaderVariants`, `emptyMediaV…
|
||||
|
||||
- empty.variants — `emptyVariants` is the cva config…
|
||||
|
||||
- [empty.variants](./empty.variants.md) — `emptyVariants` is the cva config that powers the `<Empty>` chassis component (with `emptyHeaderVariants`, `emptyMediaV…
|
||||
- [empty.variants](./empty.variants.md) — `emptyVariants` is the cva config that powers the `<Empty>` chassis component (with `emptyHeaderVariants`, `emptyMediaV…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Empty>` is the chassis empty-state primitive for `@suaveplan/react-ui`. It mirrors the upstream shadcn `empty` registry item (per Wave 3a's reconciliation finding #6) and ships as a family of six components — `<Empty>` (root), `<EmptyHeader>`, `<EmptyMedia>`, `<EmptyTitle>`, `<EmptyDescription>`, `<EmptyContent>` — that compose into the conventional empty-state layout. Per the shadcn skill rule, ALWAYS use `<Empty>` for empty states rather than building custom empty-state markup — hand-rolled empty states diverge across teams in spacing, iconography, and CTA layout.
|
||||
|
||||
The root `<Empty>` exposes a `size` axis with three rungs (`sm` / `default` / `lg`) that control vertical breathing room. The component renders `role="status"` with `aria-live="polite"` so assistive tech announces the empty state when it appears (e.g. when a search returns no results) without interrupting the user.
|
||||
|
||||
## Features
|
||||
|
||||
- Six composition primitives — `Empty`, `EmptyHeader`, `EmptyMedia`, `EmptyTitle`, `EmptyDescription`, `EmptyContent`.
|
||||
- Three sizes via the `size` axis (`sm` / `default` / `lg`) on the root.
|
||||
- `role="status"` + `aria-live="polite"` so AT announces the empty state when it appears.
|
||||
- `<EmptyMedia>` is `aria-hidden="true"` because the title / description carries the accessible name.
|
||||
- `<EmptyTitle>` renders as `<h3>`, `<EmptyDescription>` renders as `<p>` so the document outline stays clean.
|
||||
- All sub-components forward refs.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Empty,
|
||||
EmptyContent,
|
||||
EmptyDescription,
|
||||
EmptyHeader,
|
||||
EmptyMedia,
|
||||
EmptyTitle,
|
||||
} from "@suaveplan/react-ui";
|
||||
import { FolderIcon } from "lucide-react";
|
||||
|
||||
export function NoProjects() {
|
||||
return (
|
||||
<Empty>
|
||||
<EmptyHeader>
|
||||
<EmptyMedia>
|
||||
<FolderIcon className="size-6" />
|
||||
</EmptyMedia>
|
||||
<EmptyTitle>No projects yet</EmptyTitle>
|
||||
<EmptyDescription>
|
||||
Get started by creating a new project.
|
||||
</EmptyDescription>
|
||||
</EmptyHeader>
|
||||
<EmptyContent>
|
||||
<button type="button">Create project</button>
|
||||
</EmptyContent>
|
||||
</Empty>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Empty,
|
||||
EmptyContent,
|
||||
EmptyDescription,
|
||||
EmptyHeader,
|
||||
EmptyMedia,
|
||||
EmptyTitle,
|
||||
} from "@suaveplan/react-ui";
|
||||
import { useTranslation } from "@suaveplan/react-i18n";
|
||||
import { InboxIcon } from "lucide-react";
|
||||
|
||||
export function EmptyInbox() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Empty size="lg">
|
||||
<EmptyHeader>
|
||||
<EmptyMedia className="bg-sky-500/10 text-sky-600">
|
||||
<InboxIcon className="size-6" />
|
||||
</EmptyMedia>
|
||||
<EmptyTitle>{t("react-ui.empty.defaultTitle")}</EmptyTitle>
|
||||
<EmptyDescription>
|
||||
{t("react-ui.empty.defaultDescription")}
|
||||
</EmptyDescription>
|
||||
</EmptyHeader>
|
||||
</Empty>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Empty>`
|
||||
|
||||
`forwardRef<HTMLDivElement, EmptyProps>` — root container. Renders `role="status"` and `aria-live="polite"`.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Vertical breathing room. |
|
||||
| _all_ `HTMLAttributes<HTMLDivElement>` | _native_ | _native_ | Forwarded as-is. |
|
||||
|
||||
### `<EmptyHeader>`
|
||||
|
||||
`forwardRef<HTMLDivElement, EmptyHeaderProps>` — flex column wrapping `<EmptyMedia>`, `<EmptyTitle>`, `<EmptyDescription>`.
|
||||
|
||||
### `<EmptyMedia>`
|
||||
|
||||
`forwardRef<HTMLDivElement, EmptyMediaProps>` — wrapper for the icon / illustration. Always `aria-hidden="true"`.
|
||||
|
||||
### `<EmptyTitle>`
|
||||
|
||||
`forwardRef<HTMLHeadingElement, EmptyTitleProps>` — heading rendered as `<h3>`.
|
||||
|
||||
### `<EmptyDescription>`
|
||||
|
||||
`forwardRef<HTMLParagraphElement, EmptyDescriptionProps>` — supplementary description rendered as `<p>`.
|
||||
|
||||
### `<EmptyContent>`
|
||||
|
||||
`forwardRef<HTMLDivElement, EmptyContentProps>` — flex row for action affordances.
|
||||
|
||||
#### Errors
|
||||
|
||||
The Empty components do NOT call `useTranslation()` so they do not interact with the i18n provider directly. The chassis ships translation keys (`react-ui.empty.defaultTitle`, `react-ui.empty.defaultDescription`, `react-ui.empty.noResults`) that consumers can use as fallbacks via `t('react-ui.empty.defaultTitle')` at the call site.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `role="status"` + `aria-live="polite"` is set unconditionally on the root so AT announces the empty state when it appears (e.g. when a search returns no results) without interrupting the user. Consumers needing a more aggressive announcement should wrap in their own `aria-live="assertive"` container; consumers wanting silence should pass `role="presentation"` to override.
|
||||
- `<EmptyMedia>` is `aria-hidden="true"` because the icon is decorative — the accessible name comes from the title and description text.
|
||||
- The chassis intentionally does NOT inject a default fallback title / description — the bundled translation keys (`react-ui.empty.defaultTitle`, `react-ui.empty.defaultDescription`) are convenience fallbacks consumers opt into via `t()`. Hardcoding fallback text would force every consumer to translate it even when their own copy already exists.
|
||||
- The `size` axis affects only the root's `gap` and `p` — sub-component spacing remains constant. This keeps the visual rhythm consistent across all empty-state sizes and means consumers can swap `size` without rewriting their composition.
|
||||
- `<EmptyContent>` uses `flex-wrap` so action buttons stack vertically on narrow viewports rather than overflow horizontally.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Empty Variants
|
||||
|
||||
## Purpose
|
||||
|
||||
`emptyVariants` is the cva config that powers the `<Empty>` chassis component (with `emptyHeaderVariants`, `emptyMediaVariants`, `emptyTitleVariants`, `emptyDescriptionVariants`, and `emptyContentVariants` for the sub-components). The root `Empty` exposes a `size` axis with three rungs (`sm` / `default` / `lg`) that control vertical breathing room — `sm` for tight surfaces (drawers, compact cards), `default` for typical empty states, `lg` for full-page empties.
|
||||
|
||||
## Features
|
||||
|
||||
- Single `size` axis with three rungs (`sm` / `default` / `lg`) on `emptyVariants` — controls gap and padding in lockstep.
|
||||
- `<EmptyMedia>` renders a `size-12 rounded-full bg-muted` circle so the icon / illustration sits naturally on the surface.
|
||||
- `<EmptyTitle>` is `text-lg font-semibold leading-none tracking-tight`; `<EmptyDescription>` is `text-sm text-muted-foreground max-w-prose` for readability.
|
||||
- `<EmptyContent>` is `flex flex-wrap items-center justify-center gap-2` so action buttons stack on narrow viewports.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { emptyVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const sm = emptyVariants({ size: "sm" });
|
||||
const default_ = emptyVariants();
|
||||
const lg = emptyVariants({ size: "lg" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { cva } from "class-variance-authority";
|
||||
import { emptyVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const fullScreenEmpty = cva(
|
||||
[emptyVariants({ size: "lg" }), "min-h-[60vh]"].join(" ")
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `emptyVariants(opts?)`
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Vertical breathing room — controls gap + padding. |
|
||||
|
||||
### Sub-component cvas
|
||||
|
||||
`emptyHeaderVariants()`, `emptyMediaVariants()`, `emptyTitleVariants()`, `emptyDescriptionVariants()`, `emptyContentVariants()` — no options.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The `size` axis affects only `gap` and `p` on the root container. Sub-component cvas have no axes because their relative spacing is consistent across all empty-state sizes.
|
||||
- `<EmptyMedia>` uses `bg-muted text-muted-foreground` so any lucide icon child inherits the muted foreground colour automatically. Consumers wanting a coloured media slot override at the call site (`<EmptyMedia className="bg-emerald-500/10 text-emerald-600">`).
|
||||
- `<EmptyDescription>` uses `max-w-prose` so long descriptions wrap at a comfortable read width rather than spanning the full container.
|
||||
- `<EmptyContent>` uses `flex-wrap` so action buttons stack vertically on narrow viewports rather than overflow horizontally.
|
||||
@@ -0,0 +1,156 @@
|
||||
# `<ErrorBoundary>`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - error-boundary.metrics — Co-located OTel counter fo…
|
||||
- [error-boundary.metrics](./error-boundary.metrics.md) — Co-located OTel counter for the chassis `<ErrorBoundary>` component. Lives next to the boundary itself rather than in t…
|
||||
- [error-boundary.test-fixtures](./error-boundary.test-fixtures.md) — Wave-3g local test fixture that augments the chassis test i18n instance with the `react-ui.errorBoundary.*` keyset. The…
|
||||
- [error-boundary.variants](./error-boundary.variants.md) — Cva slot definitions for the chassis `<ErrorBoundary>` default fallback. Splits the fallback into four named slots — ou…
|
||||
- error-boundary.metrics — Co-located OTel counter fo…
|
||||
|
||||
- [error-boundary.metrics](./error-boundary.metrics.md) — Co-located OTel counter for the chassis `<ErrorBoundary>` component. Lives next to the boundary itself rather than in t…
|
||||
- [error-boundary.test-fixtures](./error-boundary.test-fixtures.md) — Wave-3g local test fixture that augments the chassis test i18n instance with the `react-ui.errorBoundary.*` keyset. The…
|
||||
- [error-boundary.variants](./error-boundary.variants.md) — Cva slot definitions for the chassis `<ErrorBoundary>` default fallback. Splits the fallback into four named slots — ou…
|
||||
|
||||
- error-boundary.metrics — Co-located OTel counter fo…
|
||||
|
||||
- [error-boundary.metrics](./error-boundary.metrics.md) — Co-located OTel counter for the chassis `<ErrorBoundary>` component. Lives next to the boundary itself rather than in t…
|
||||
- [error-boundary.test-fixtures](./error-boundary.test-fixtures.md) — Wave-3g local test fixture that augments the chassis test i18n instance with the `react-ui.errorBoundary.*` keyset. The…
|
||||
- [error-boundary.variants](./error-boundary.variants.md) — Cva slot definitions for the chassis `<ErrorBoundary>` default fallback. Splits the fallback into four named slots — ou…
|
||||
|
||||
- error-boundary.metrics — Co-located OTel counter fo…
|
||||
|
||||
- [error-boundary.metrics](./error-boundary.metrics.md) — Co-located OTel counter for the chassis `<ErrorBoundary>` component. Lives next to the boundary itself rather than in t…
|
||||
- [error-boundary.test-fixtures](./error-boundary.test-fixtures.md) — Wave-3g local test fixture that augments the chassis test i18n instance with the `react-ui.errorBoundary.*` keyset. The…
|
||||
- [error-boundary.variants](./error-boundary.variants.md) — Cva slot definitions for the chassis `<ErrorBoundary>` default fallback. Splits the fallback into four named slots — ou…
|
||||
- [error-boundary.metrics](./error-boundary.metrics.md) — Co-located OTel counter for the chassis `<ErrorBoundary>` component. Lives next to the boundary itself rather than in t…
|
||||
- [error-boundary.test-fixtures](./error-boundary.test-fixtures.md) — Wave-3g local test fixture that augments the chassis test i18n instance with the `react-ui.errorBoundary.*` keyset. The…
|
||||
- [error-boundary.variants](./error-boundary.variants.md) — Cva slot definitions for the chassis `<ErrorBoundary>` default fallback. Splits the fallback into four named slots — ou…
|
||||
|
||||
## Purpose
|
||||
|
||||
Chassis class-based React error boundary. Catches render-time errors
|
||||
thrown by descendants and either surfaces a consumer-supplied
|
||||
`fallback` or renders the chassis default fallback (centred card with
|
||||
title / description / Retry button keyed on bundled
|
||||
`react-ui.errorBoundary.*` translation keys). Every capture
|
||||
increments `react_ui.error_boundary.caught{error.name=...}` so error
|
||||
hot spots show up in observability without consumer-side ceremony.
|
||||
|
||||
## Features
|
||||
|
||||
- Class-based by necessity — React's error-boundary lifecycle
|
||||
(`getDerivedStateFromError` + `componentDidCatch`) is class-only.
|
||||
- Optional `fallback` accepts either a `ReactNode` (rendered as-is)
|
||||
or a render-prop `(props: { error, reset }) => ReactNode`.
|
||||
- Optional `onError(error, info)` for forwarding to Sentry / OTel
|
||||
exception events / custom loggers.
|
||||
- `reset()` API exposed to render-prop fallbacks for retry UX.
|
||||
- Default fallback uses the chassis card surface with a Retry
|
||||
button keyed on `react-ui.errorBoundary.retry`.
|
||||
- Increments `react_ui.error_boundary.caught` counter with
|
||||
`error.name` and best-effort `origin.component` attributes.
|
||||
- NEVER swallows errors silently — always either fires `onError` OR
|
||||
increments the counter (and typically both).
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { ErrorBoundary } from "@suaveplan/react-ui";
|
||||
|
||||
function App(): JSX.Element {
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<RiskyChild />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { ErrorBoundary } from "@suaveplan/react-ui";
|
||||
|
||||
function CheckoutPage(): JSX.Element {
|
||||
return (
|
||||
<ErrorBoundary
|
||||
onError={(error, info) => {
|
||||
// Forward to Sentry, then to OTel exception event:
|
||||
Sentry.captureException(error, { contexts: { react: info } });
|
||||
}}
|
||||
fallback={({ error, reset }) => (
|
||||
<div role="alert" className="p-6">
|
||||
<h2 className="text-lg font-bold">Checkout error</h2>
|
||||
<pre className="text-sm">{error.message}</pre>
|
||||
<button type="button" onClick={reset}>
|
||||
Try again
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<CheckoutForm />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<ErrorBoundary>` props
|
||||
|
||||
- `children: ReactNode` — REQUIRED. The subtree to guard.
|
||||
- `fallback?: ReactNode | ((props: { error, reset }) => ReactNode)` —
|
||||
optional. When omitted, the chassis default fallback renders.
|
||||
- `onError?: (error: Error, info: ErrorInfo) => void` — optional.
|
||||
Invoked synchronously from `componentDidCatch`, BEFORE the chassis
|
||||
counter increment.
|
||||
- `className?: string` — optional. Applied to the default fallback
|
||||
root. Has no effect when a custom `fallback` is supplied.
|
||||
|
||||
### `ErrorBoundaryFallbackProps` (render-prop arguments)
|
||||
|
||||
- `error: Error` — the captured error.
|
||||
- `reset: () => void` — clears the captured-error state and
|
||||
re-mounts `children` from the next render.
|
||||
|
||||
### Metrics
|
||||
|
||||
- `react_ui.error_boundary.caught` counter — incremented on every
|
||||
capture. Attributes:
|
||||
- `error.name` — always present.
|
||||
- `origin.component` — best-effort component-stack leaf, when
|
||||
`ErrorInfo.componentStack` carries a parseable owner name.
|
||||
|
||||
### Metrics helpers (re-exported from sibling module)
|
||||
|
||||
- `recordErrorBoundaryCaught({ errorName, originComponent? })` —
|
||||
manual counter emit, used internally by the boundary and exposed
|
||||
for advanced consumers (custom error handlers that bypass the
|
||||
boundary).
|
||||
- `getErrorBoundaryMetricsState()` — snapshot accessor for tests.
|
||||
- `resetErrorBoundaryMetricsState()` — clears cached instruments
|
||||
and the in-memory state ring. Safe to call between tests.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The boundary deliberately does NOT log to `console.error` from
|
||||
`componentDidCatch` — React itself logs the error in development;
|
||||
the chassis adds OTel attribution and forwards to the consumer's
|
||||
optional handler. Doubling up the console emission would be noisy
|
||||
and inconsistent with the chassis policy of "consumer owns
|
||||
observability surface".
|
||||
- `getDerivedStateFromError` runs in the render phase and is
|
||||
side-effect-free, per React docs. Counter incrementation lives in
|
||||
`componentDidCatch` (the commit phase) so the side-effect is
|
||||
deferred safely.
|
||||
- `reset()` increments a `resetGeneration` counter alongside
|
||||
clearing the error so consumers can derive "retry attempts" UI
|
||||
from the state without storing additional flags.
|
||||
- `forwardRef` does not apply — class components do not return
|
||||
refs to their consumers via the `forwardRef` mechanism. Consumers
|
||||
wanting a ref into the guarded subtree mount their own
|
||||
ref-forwarding wrapper INSIDE the boundary.
|
||||
- The default fallback is a separate function component so the
|
||||
`useTranslation` / `useThemedVariants` hooks live in a hook-safe
|
||||
context (the boundary class cannot call hooks itself).
|
||||
@@ -0,0 +1,75 @@
|
||||
# `error-boundary.metrics`
|
||||
|
||||
## Purpose
|
||||
|
||||
Co-located OTel counter for the chassis `<ErrorBoundary>` component.
|
||||
Lives next to the boundary itself rather than in the package-wide
|
||||
`observability/react-ui-metrics.ts` so the Wave 3g batch can ship
|
||||
without modifying the shared metrics aggregator file. The central
|
||||
wireup commit at the end of the parallel-wave window folds this
|
||||
counter into the canonical module.
|
||||
|
||||
## Features
|
||||
|
||||
- Single counter: `react_ui.error_boundary.caught`.
|
||||
- Attributes: `error.name` (always), `origin.component` (when
|
||||
React's `ErrorInfo.componentStack` carries a leading owner name).
|
||||
- In-memory state ring matches the canonical module's
|
||||
`ReactUiMetricsState` array-of-samples shape so spec-conformance
|
||||
tests slot in unchanged after the wireup merge.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { recordErrorBoundaryCaught } from "@suaveplan/react-ui/components/error-boundary/error-boundary.metrics";
|
||||
|
||||
recordErrorBoundaryCaught({
|
||||
errorName: "TypeError",
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
getErrorBoundaryMetricsState,
|
||||
recordErrorBoundaryCaught,
|
||||
resetErrorBoundaryMetricsState,
|
||||
} from "@suaveplan/react-ui/components/error-boundary/error-boundary.metrics";
|
||||
|
||||
resetErrorBoundaryMetricsState();
|
||||
recordErrorBoundaryCaught({
|
||||
errorName: "RangeError",
|
||||
originComponent: "VaultItemEditor",
|
||||
});
|
||||
const samples = getErrorBoundaryMetricsState().errorBoundaryCaughtTotal;
|
||||
console.assert(samples.length === 1);
|
||||
console.assert(samples[0]?.attributes["error.name"] === "RangeError");
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `recordErrorBoundaryCaught(attributes): void` — increments the
|
||||
counter and pushes a sample onto the in-memory state ring.
|
||||
- `errorName: string` — REQUIRED. `error.name`, ideally a stable
|
||||
constant ("TypeError", "RangeError", custom subclass name).
|
||||
- `originComponent?: string` — optional component-stack leaf.
|
||||
- `getErrorBoundaryMetricsState(): ErrorBoundaryMetricsState` —
|
||||
snapshot accessor used by tests.
|
||||
- `resetErrorBoundaryMetricsState(): void` — clears the cached
|
||||
meter instruments and the state ring. Safe to call between tests.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The counter name aligns with the chassis convention
|
||||
(`react_ui.<surface>.<event>`) so the eventual merge into
|
||||
`react-ui-metrics.ts` is a name-preserving move.
|
||||
- The instruments are cached lazily so we never construct a meter
|
||||
outside a test fixture or a real production tracer pipeline.
|
||||
- The state ring uses an unbounded `push`. Tests reset it via
|
||||
`resetErrorBoundaryMetricsState()` between cases; in production the
|
||||
ring is read once on shutdown and discarded.
|
||||
- The component-stack origin is best-effort — React's `ErrorInfo`
|
||||
carries a stack string whose format is implementation-dependent;
|
||||
the boundary parses the leading owner name when present and omits
|
||||
the attribute otherwise.
|
||||
@@ -0,0 +1,64 @@
|
||||
# error-boundary.test-fixtures
|
||||
|
||||
## Purpose
|
||||
|
||||
Wave-3g local test fixture that augments the chassis test i18n instance with
|
||||
the `react-ui.errorBoundary.*` keyset. The chassis spec tests under
|
||||
`error-boundary.test.tsx` need to assert on the resolved English strings
|
||||
("Something went wrong", "An unexpected error occurred. Please try again.",
|
||||
"Retry") before the central wireup commit folds the canonical translations into
|
||||
the bundled `en-GB.json` source-of-truth. Once the wireup commit lands, the
|
||||
bundled keys shadow the fixture and `augmentI18nWithErrorBoundaryKeys` becomes
|
||||
a no-op re-merge — the fixture stays in place during the deletion window so
|
||||
the deletion can land as a single atomic cleanup pass without breaking the
|
||||
suite mid-rebase.
|
||||
|
||||
## Features
|
||||
|
||||
- Bundles localized `errorBoundary` strings for all five chassis-supported
|
||||
locales (`en-GB`, `zh-CN`, `ar-AE`, `cy-GB`, `ru-RU`)
|
||||
- Idempotent merge — calling the augment function twice against the same
|
||||
instance is a no-op
|
||||
- Zero coupling to the fixture's call-site structure: the augment helper accepts
|
||||
any `I18nInstance`, so test setups can compose it freely with other fixtures
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
|
||||
```ts
|
||||
import { createTestI18n } from "../../../tests/setup/test-i18n.js";
|
||||
import { augmentI18nWithErrorBoundaryKeys } from "./error-boundary.test-fixtures.js";
|
||||
|
||||
const i18n = await createTestI18n({ locale: "en-GB" });
|
||||
augmentI18nWithErrorBoundaryKeys(i18n);
|
||||
```
|
||||
|
||||
### Advanced Example
|
||||
|
||||
```ts
|
||||
import { createTestI18n } from "../../../tests/setup/test-i18n.js";
|
||||
import { augmentI18nWithErrorBoundaryKeys } from "./error-boundary.test-fixtures.js";
|
||||
|
||||
for (const locale of ["en-GB", "ar-AE", "cy-GB"] as const) {
|
||||
const i18n = await createTestI18n({ locale });
|
||||
augmentI18nWithErrorBoundaryKeys(i18n);
|
||||
// Assert against the locale-specific resolved strings.
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `augmentI18nWithErrorBoundaryKeys(i18n: I18nInstance): void`
|
||||
|
||||
Merges the bundled `errorBoundary` keyset into the supplied i18n instance for
|
||||
every supported locale. Returns nothing. Idempotent.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The fixture is intentionally tiny — five locale × three-key bundles plus a
|
||||
loop. Once the wireup commit folds the canonical keys into the chassis
|
||||
`en-GB.json` source-of-truth, this file will be removed alongside its `.md`
|
||||
sibling, the spec test imports rewritten to drop the
|
||||
`augmentI18nWithErrorBoundaryKeys` call, and the change archived as part of
|
||||
the Wave 3g cleanup pass.
|
||||
@@ -0,0 +1,71 @@
|
||||
# `error-boundary.variants`
|
||||
|
||||
## Purpose
|
||||
|
||||
Cva slot definitions for the chassis `<ErrorBoundary>` default
|
||||
fallback. Splits the fallback into four named slots — outer
|
||||
container, title, description, retry button — so brand themes can
|
||||
override individual treatments via `ThemePlugin.variantOverrides`
|
||||
without needing to ship a custom `fallback` prop on every ErrorBoundary
|
||||
mount in the app.
|
||||
|
||||
## Features
|
||||
|
||||
- Four registered slots: `ErrorBoundaryFallback`,
|
||||
`ErrorBoundaryTitle`, `ErrorBoundaryDescription`,
|
||||
`ErrorBoundaryRetry`.
|
||||
- Default styling matches the chassis card surface (rounded,
|
||||
bordered, shadowed) so the failure UI reads as a discrete panel.
|
||||
- Retry button matches `<Button variant="default">` so the primary
|
||||
action treatment is identical to the rest of the chassis.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
errorBoundaryFallbackVariants,
|
||||
errorBoundaryTitleVariants,
|
||||
} from "@suaveplan/react-ui/components/error-boundary/error-boundary";
|
||||
|
||||
const fallbackClass = errorBoundaryFallbackVariants();
|
||||
const titleClass = errorBoundaryTitleVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { defineVariants } from "@suaveplan/react-ui/variants";
|
||||
import type { ThemePlugin } from "@suaveplan/react-ui";
|
||||
|
||||
const brandTheme: ThemePlugin = {
|
||||
name: "brand",
|
||||
tokens: { /* ... */ },
|
||||
variantOverrides: {
|
||||
ErrorBoundaryFallback: defineVariants(
|
||||
"ErrorBoundaryFallback",
|
||||
"flex flex-col items-center bg-surface p-8",
|
||||
{ variants: {}, defaultVariants: {} }
|
||||
),
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `errorBoundaryFallbackVariants(): string` — outer container shape.
|
||||
- `errorBoundaryTitleVariants(): string` — heading.
|
||||
- `errorBoundaryDescriptionVariants(): string` — body text.
|
||||
- `errorBoundaryRetryVariants(): string` — Retry button.
|
||||
|
||||
All resolvers take no variants by default; brand themes can attach
|
||||
new variant axes via the override mechanism.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The fallback is intentionally split into four slots rather than one
|
||||
monolithic shape because brand themes typically want to keep the
|
||||
default copy and behaviour while restyling the visual treatment. A
|
||||
single-slot design would force theme authors to re-render the entire
|
||||
fallback subtree, which would also lose the chassis-supplied
|
||||
translation keys. Splitting per slot lets the theme override only the
|
||||
visual leaves while leaving content + structure under chassis control.
|
||||
116
docs/ui/react/react-ui/src/components/field-group/field-group.md
Normal file
116
docs/ui/react/react-ui/src/components/field-group/field-group.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# FieldGroup
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - field-group.variants — `fieldGroupVariants` is the……
|
||||
- [field-group.variants](./field-group.variants.md) — `fieldGroupVariants` is the cva (class-variance-authority) configuration powering every `<FieldGroup>` render. It is sp…
|
||||
- field-group.variants — `fieldGroupVariants` is the……
|
||||
|
||||
- [field-group.variants](./field-group.variants.md) — `fieldGroupVariants` is the cva (class-variance-authority) configuration powering every `<FieldGroup>` render. It is sp…
|
||||
|
||||
- field-group.variants — `fieldGroupVariants` is the……
|
||||
|
||||
- [field-group.variants](./field-group.variants.md) — `fieldGroupVariants` is the cva (class-variance-authority) configuration powering every `<FieldGroup>` render. It is sp…
|
||||
|
||||
- field-group.variants — `fieldGroupVariants` is the…
|
||||
|
||||
- [field-group.variants](./field-group.variants.md) — `fieldGroupVariants` is the cva (class-variance-authority) configuration powering every `<FieldGroup>` render. It is sp…
|
||||
- [field-group.variants](./field-group.variants.md) — `fieldGroupVariants` is the cva (class-variance-authority) configuration powering every `<FieldGroup>` render. It is sp…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<FieldGroup>` is the chassis vertical-stack container for `<Field>` instances. It renders a `<div>` with `flex flex-col gap-*` so siblings flow in document order regardless of writing direction (RTL / LTR). Per the shadcn skill rule the chassis NEVER uses `space-y-*` because it collapses cross-axis margin in unpredictable ways under RTL.
|
||||
|
||||
Per the cross-cutting reconciliation note (#1), shadcn ships `field-group` as a separate registry item from `field`; this package mirrors that split so each primitive evolves independently. `<FieldGroup>` is the typical container for CRUD forms — multiple `<Field>` instances stacked with a uniform gap.
|
||||
|
||||
The `gap` axis lets consumers tune spacing without reaching for arbitrary Tailwind classes: `sm` (8px) for dense settings panes, `default` (16px) for typical CRUD forms, `lg` (24px) for sectioned profile pages.
|
||||
|
||||
## Features
|
||||
|
||||
- Three gap sizes (`sm` → `gap-2`, `default` → `gap-4`, `lg` → `gap-6`).
|
||||
- `flex flex-col` layout so siblings stack in document order under both LTR and RTL.
|
||||
- Native HTML attributes forwarded — consumers drop `role="group" aria-labelledby="…"` for accessible grouping.
|
||||
- Refs forwarded to the underlying `<div>`.
|
||||
- Theme-driven variant overrides via `useThemedVariants("FieldGroup", fieldGroupVariants)`.
|
||||
- NEVER uses `space-y-*` per the shadcn skill rule.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { FieldGroup, Field, FieldLabel, Input } from "@suaveplan/react-ui";
|
||||
|
||||
export function ContactForm() {
|
||||
return (
|
||||
<FieldGroup>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="email">Email</FieldLabel>
|
||||
<Input id="email" type="email" />
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="phone">Phone</FieldLabel>
|
||||
<Input id="phone" type="tel" />
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
FieldGroup,
|
||||
Field,
|
||||
FieldLabel,
|
||||
FieldDescription,
|
||||
Input,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function ProfileSection() {
|
||||
return (
|
||||
<section aria-labelledby="profile-h">
|
||||
<h2 id="profile-h">Profile</h2>
|
||||
<FieldGroup gap="lg" role="group" aria-labelledby="profile-h">
|
||||
<Field>
|
||||
<FieldLabel htmlFor="display-name">Display name</FieldLabel>
|
||||
<Input id="display-name" />
|
||||
<FieldDescription>Visible to other users.</FieldDescription>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="bio">Biography</FieldLabel>
|
||||
<Input id="bio" />
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="website">Website</FieldLabel>
|
||||
<Input id="website" type="url" />
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<FieldGroup>`
|
||||
|
||||
`forwardRef<HTMLDivElement, FieldGroupProps>` — renders a `<div>` container.
|
||||
|
||||
#### `FieldGroupProps`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `gap` | `"sm" \| "default" \| "lg"` | `"default"` | Spacing between sibling `<Field>` instances. |
|
||||
| _all native_ `HTMLAttributes<HTMLDivElement>` | _native_ | _native_ | Forwarded as-is. |
|
||||
|
||||
#### `fieldGroupVariants`
|
||||
|
||||
The bare cva resolver. Exported so external consumers can re-use the variants directly.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The container is intentionally minimal — layout (`flex flex-col`), gap, and forwarded native attrs only. Consumers that need an explicit ARIA grouping role pass `role="group"` + `aria-labelledby` themselves.
|
||||
- Per the shadcn skill rule, the cva NEVER uses `space-y-*` — `gap-*` flows the same in RTL and consumer-applied flex utilities stack predictably.
|
||||
- Per the cross-cutting reconciliation note (#1), this is a separate registry item from `field`. Keeping them split lets a consumer eject only `field` if they want to compose their own group container.
|
||||
- Refs target the `<div>` — consumers can measure or scroll-into-view the group programmatically.
|
||||
- The `gap` axis maps directly to Tailwind's gap scale; consumers wanting an arbitrary gap pass `className="gap-3"` (Tailwind class merging via `tailwind-merge` resolves the conflict against the cva default).
|
||||
@@ -0,0 +1,55 @@
|
||||
# FieldGroup Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`fieldGroupVariants` is the cva (class-variance-authority) configuration powering every `<FieldGroup>` render. It is split from `field-group.tsx` into its own module so an ejected source copy under `npx shadcn@latest add @suaveplan/field-group` keeps the cva configuration in a dedicated file. Theme overrides flow through `useThemedVariants("FieldGroup", fieldGroupVariants)`.
|
||||
|
||||
## Features
|
||||
|
||||
- Three gap sizes (`sm` → `gap-2`, `default` → `gap-4`, `lg` → `gap-6`).
|
||||
- `flex flex-col` base layout — siblings stack in document order under both LTR and RTL.
|
||||
- NEVER uses `space-y-*` per the shadcn skill rule.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { fieldGroupVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const className = fieldGroupVariants({ gap: "default" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { fieldGroupVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomFieldGroup({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
role="group"
|
||||
className={cn(fieldGroupVariants({ gap: "lg" }), "rounded-lg p-4 border")}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `fieldGroupVariants(props?: { gap?: "sm" | "default" | "lg" }): string`
|
||||
|
||||
Returns the cva-merged class string for the supplied gap. Omitted props fall back to the cva `defaultVariants` (`gap: "default"`).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The resolver is tagged with `componentName: "FieldGroup"` (non-enumerable, non-writable) via `defineVariants` so debug tooling can identify the cva instance at runtime alongside the other chassis variant resolvers.
|
||||
- Per the shadcn skill rule, the cva NEVER uses `space-y-*` — `gap-*` flows the same in RTL and consumer-applied flex utilities stack predictably without the cross-axis margin collapse that plagues the upstream shadcn baseline. Tailwind v4 resolves the gap utility against the active container's writing direction natively.
|
||||
- Consumers wanting a horizontal layout (rare for forms) override via `className="flex-row"` — `tailwind-merge` resolves the conflict against the cva `flex-col` automatically. The chassis does NOT expose a `direction` axis because horizontal field groups are an anti-pattern for accessibility (label / control reading order breaks).
|
||||
- The three gap sizes map directly to the Tailwind gap scale (`gap-2` = 8px, `gap-4` = 16px, `gap-6` = 24px). Consumers needing an arbitrary intermediate value pass `className="gap-3"` — the cva default loses to the explicit class via the standard `tailwind-merge` resolution order.
|
||||
- The base layout (`flex flex-col`) is part of the cva default rather than the consumer's responsibility because every `<FieldGroup>` use case stacks vertically. Consumers wanting to escape this default override via `className="flex-row"` or `className="grid grid-cols-2"` for side-by-side layouts.
|
||||
- The cva config exposes only one axis (`gap`) because the layout is otherwise consumer-owned — alignment, padding, and width are NOT part of the chassis surface. Themes that want to add a `density` axis layered atop `gap` register via `variantOverrides.FieldGroup` and the chassis composition resolves the merge.
|
||||
131
docs/ui/react/react-ui/src/components/field-set/field-set.md
Normal file
131
docs/ui/react/react-ui/src/components/field-set/field-set.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# FieldSet (suaveplan-only)
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - field-set.variants — `fieldSetVariants` and `fieldL…
|
||||
- [field-set.variants](./field-set.variants.md) — `fieldSetVariants` and `fieldLegendVariants` are the cva (class-variance-authority) configurations powering `<FieldSet>…
|
||||
- field-set.variants — `fieldSetVariants` and `fieldL…
|
||||
|
||||
- [field-set.variants](./field-set.variants.md) — `fieldSetVariants` and `fieldLegendVariants` are the cva (class-variance-authority) configurations powering `<FieldSet>…
|
||||
|
||||
- field-set.variants — `fieldSetVariants` and `fieldL…
|
||||
|
||||
- [field-set.variants](./field-set.variants.md) — `fieldSetVariants` and `fieldLegendVariants` are the cva (class-variance-authority) configurations powering `<FieldSet>…
|
||||
|
||||
- field-set.variants — `fieldSetVariants` and `fieldL…
|
||||
|
||||
- [field-set.variants](./field-set.variants.md) — `fieldSetVariants` and `fieldLegendVariants` are the cva (class-variance-authority) configurations powering `<FieldSet>…
|
||||
- [field-set.variants](./field-set.variants.md) — `fieldSetVariants` and `fieldLegendVariants` are the cva (class-variance-authority) configurations powering `<FieldSet>…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<FieldSet>` and `<FieldLegend>` are chassis primitives that wrap related controls (typically Checkbox / RadioGroup / Switch groups) inside the native `<fieldset>` + `<legend>` HTML5 elements. The native `<fieldset disabled>` attribute propagates to every contained form control automatically, which `<Field>` (a `<div>`) cannot replicate. Pair `<FieldLegend>` with `<FieldSet>` to provide an accessible grouping heading announced by screen readers when navigating into any contained control.
|
||||
|
||||
**Suaveplan addition** — shadcn does not ship a `FieldSet` registry item upstream. The chassis adds these primitives because the cross-cutting reconciliation note (#1) flagged the gap: `<Field>` is a `<div>` and cannot carry the native `<fieldset>` semantics. Documented as suaveplan-only so consumers know the source of truth lives in this package, not upstream shadcn.
|
||||
|
||||
## Features
|
||||
|
||||
- `<FieldSet>` renders the native `<fieldset>` so `disabled` propagates to every contained control via HTML5 semantics.
|
||||
- `<FieldLegend>` renders the native `<legend>` so screen readers announce the grouping when navigating into any contained control.
|
||||
- `data-invalid="true"` on `<FieldSet>` flips descendant `<FieldLegend>` colour to destructive via the cva `data-[invalid=true]:` selector.
|
||||
- `data-disabled="true"` mirrors the native `disabled` so sibling cva selectors can react.
|
||||
- Refs forwarded on both primitives.
|
||||
- Theme-driven variant overrides via `useThemedVariants("FieldSet" | "FieldLegend")`.
|
||||
- Layout uses `flex flex-col gap-3` (NOT `space-y-*` per shadcn skill rule).
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { FieldSet, FieldLegend, Checkbox } from "@suaveplan/react-ui";
|
||||
|
||||
export function PermissionsFieldSet() {
|
||||
return (
|
||||
<FieldSet>
|
||||
<FieldLegend>Permissions</FieldLegend>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox id="read" defaultChecked />
|
||||
<label htmlFor="read">Read</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox id="write" />
|
||||
<label htmlFor="write">Write</label>
|
||||
</div>
|
||||
</FieldSet>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
FieldSet,
|
||||
FieldLegend,
|
||||
RadioGroup,
|
||||
RadioGroupItem,
|
||||
} from "@suaveplan/react-ui";
|
||||
import { useState } from "react";
|
||||
|
||||
export function NotificationFrequency() {
|
||||
const [enabled, setEnabled] = useState(true);
|
||||
const [freq, setFreq] = useState("daily");
|
||||
|
||||
// Disabling the entire fieldset propagates `disabled` to every
|
||||
// contained <RadioGroupItem> via HTML5 fieldset semantics.
|
||||
return (
|
||||
<FieldSet disabled={!enabled}>
|
||||
<FieldLegend>Notification frequency</FieldLegend>
|
||||
<RadioGroup value={freq} onValueChange={setFreq}>
|
||||
<div className="flex items-center gap-2">
|
||||
<RadioGroupItem value="daily" id="freq-daily" />
|
||||
<label htmlFor="freq-daily">Daily digest</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<RadioGroupItem value="weekly" id="freq-weekly" />
|
||||
<label htmlFor="freq-weekly">Weekly summary</label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</FieldSet>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<FieldSet>`
|
||||
|
||||
`forwardRef<HTMLFieldSetElement, FieldSetProps>` — renders the native `<fieldset>`.
|
||||
|
||||
#### `FieldSetProps`
|
||||
|
||||
Inherits every prop from `FieldsetHTMLAttributes<HTMLFieldSetElement>`. Adds:
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `invalid` | `boolean` | `false` | Drives `data-invalid="true"` on the container so descendant `<FieldLegend>` flips to destructive colour. |
|
||||
|
||||
The native `disabled` attribute is forwarded as-is and additionally drives `data-disabled="true"` for cva selectors.
|
||||
|
||||
### `<FieldLegend>`
|
||||
|
||||
`forwardRef<HTMLLegendElement, FieldLegendProps>` — renders the native `<legend>`.
|
||||
|
||||
#### `FieldLegendProps`
|
||||
|
||||
Inherits every prop from `HTMLAttributes<HTMLLegendElement>`. Adds:
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `invalid` | `boolean` | `false` | Drives `data-invalid="true"` on the legend itself so the destructive colour activates without depending on a parent `<FieldSet invalid>`. |
|
||||
|
||||
#### Cva resolvers
|
||||
|
||||
`fieldSetVariants` and `fieldLegendVariants` are exported as bare cva resolvers for external composition.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- This is a suaveplan-only addition — shadcn does not ship a `field-set` registry item upstream. The chassis adds it because `<Field>` (a `<div>`) cannot carry the native `<fieldset>` semantics, and the cross-cutting reconciliation note (#1) flagged the gap.
|
||||
- The native `<fieldset disabled>` attribute propagates `disabled` to every contained form control automatically. The chassis preserves this semantic by rendering the actual HTML5 element rather than wrapping in a `<div role="group">`.
|
||||
- The `<FieldLegend>` `invalid` prop is exposed independently of `<FieldSet>` so consumers can render a legend in error state outside a fieldset (e.g. a custom layout that uses the chassis legend styling without the fieldset wrapper).
|
||||
- Per the shadcn skill rule, the cva NEVER uses `space-y-*` — `gap-3` flows the same in RTL.
|
||||
- Refs target the native `<fieldset>` / `<legend>` so consumers can measure or scroll-into-view programmatically.
|
||||
@@ -0,0 +1,59 @@
|
||||
# FieldSet Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`fieldSetVariants` and `fieldLegendVariants` are the cva (class-variance-authority) configurations powering `<FieldSet>` and `<FieldLegend>`. They are split from `field-set.tsx` into their own module so an ejected source copy keeps the cva configurations in a dedicated file. Theme overrides flow through `useThemedVariants("FieldSet" | "FieldLegend")`.
|
||||
|
||||
These cvas are part of the suaveplan-only `<FieldSet>` addition (no shadcn upstream) — see `field-set.md` for the rationale.
|
||||
|
||||
## Features
|
||||
|
||||
- `<FieldSet>` carries `flex flex-col gap-3 rounded-md border p-4` so the grouping is visually obvious.
|
||||
- `data-[disabled=true]:opacity-70` decay when disabled.
|
||||
- `<FieldLegend>` is small bold text (`text-sm font-semibold`) with `data-[invalid=true]:text-destructive` for the error state.
|
||||
- NEVER uses `space-y-*` per the shadcn skill rule.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { fieldSetVariants, fieldLegendVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const fsClass = fieldSetVariants();
|
||||
const lgClass = fieldLegendVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { fieldSetVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomFieldSet({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<fieldset
|
||||
className={cn(fieldSetVariants(), "shadow-md ring-1 ring-accent")}
|
||||
>
|
||||
{children}
|
||||
</fieldset>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `fieldSetVariants(): string`
|
||||
|
||||
Returns the fieldset classes.
|
||||
|
||||
### `fieldLegendVariants(): string`
|
||||
|
||||
Returns the legend classes including the `data-[invalid=true]:text-destructive` selector.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The `<fieldset>` border + padding are intentionally part of the cva default — most `<FieldSet>` use cases want the visual grouping. Consumers wanting an unstyled fieldset pass `className="border-0 p-0"` (Tailwind class merging via `tailwind-merge` resolves the conflict).
|
||||
- The legend's destructive colour is keyed off `data-[invalid=true]` directly rather than via a group selector so the legend can be used outside a `<FieldSet>` without losing the error styling.
|
||||
- Per the shadcn skill rule, the cva NEVER uses `space-y-*` — `gap-3` flows the same in RTL.
|
||||
141
docs/ui/react/react-ui/src/components/field/field.md
Normal file
141
docs/ui/react/react-ui/src/components/field/field.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# Field
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - field.variants — `fieldVariants`, `fieldLabelVarian…
|
||||
- [field.variants](./field.variants.md) — `fieldVariants`, `fieldLabelVariants`, `fieldDescriptionVariants`, and `fieldErrorVariants` are the four cva (class-var…
|
||||
- field.variants — `fieldVariants`, `fieldLabelVarian…
|
||||
|
||||
- [field.variants](./field.variants.md) — `fieldVariants`, `fieldLabelVariants`, `fieldDescriptionVariants`, and `fieldErrorVariants` are the four cva (class-var…
|
||||
|
||||
- field.variants — `fieldVariants`, `fieldLabelVarian…
|
||||
|
||||
- [field.variants](./field.variants.md) — `fieldVariants`, `fieldLabelVariants`, `fieldDescriptionVariants`, and `fieldErrorVariants` are the four cva (class-var…
|
||||
|
||||
- field.variants — `fieldVariants`, `fieldLabelVarian…
|
||||
|
||||
- [field.variants](./field.variants.md) — `fieldVariants`, `fieldLabelVariants`, `fieldDescriptionVariants`, and `fieldErrorVariants` are the four cva (class-var…
|
||||
- [field.variants](./field.variants.md) — `fieldVariants`, `fieldLabelVariants`, `fieldDescriptionVariants`, and `fieldErrorVariants` are the four cva (class-var…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Field>` is the chassis form-field container — a semantic wrapper around a single form control plus its label, description, and error message. It carries the `data-invalid` and `data-disabled` selectors so descendant `<FieldLabel>` instances pick up the destructive text colour and opacity decay automatically through the cva `group-data-[invalid=true]/field:` selector.
|
||||
|
||||
The four primitives in this module — `<Field>`, `<FieldLabel>`, `<FieldDescription>`, `<FieldError>` — compose into a complete form-field surface. Per the cross-cutting reconciliation note (#1) shadcn ships `field` as a separate registry item from `field-group`; this package mirrors that split so each primitive evolves independently.
|
||||
|
||||
`<Field>` does NOT auto-detect child invalid/disabled state — consumers pass the booleans explicitly. This keeps the chassis a pure styling primitive: the form library (sibling change `enterprise-react-forms-v2`) owns the validation orchestration and forwards `invalid` / `disabled` based on its own state.
|
||||
|
||||
## Features
|
||||
|
||||
- `<Field>` container with `flex flex-col gap-2` layout (NOT `space-y-*` per shadcn skill rule).
|
||||
- `<FieldLabel>` built on `@radix-ui/react-label` so clicking the label focuses the matched input.
|
||||
- `<FieldDescription>` renders a muted `<p>` for helper text — typically wired to the control via `aria-describedby`.
|
||||
- `<FieldError>` renders a destructive `<p role="alert">` so screen readers announce the message immediately when it appears.
|
||||
- `data-invalid` on `<Field>` automatically flips descendant `<FieldLabel>` colour to destructive.
|
||||
- `data-disabled` on `<Field>` decays opacity.
|
||||
- Refs forwarded on every primitive.
|
||||
- Theme-driven variant overrides via `useThemedVariants("Field" | "FieldLabel" | "FieldDescription" | "FieldError")`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { Field, FieldLabel, Input } from "@suaveplan/react-ui";
|
||||
|
||||
export function NameField() {
|
||||
return (
|
||||
<Field>
|
||||
<FieldLabel htmlFor="name">Name</FieldLabel>
|
||||
<Input id="name" />
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Field,
|
||||
FieldDescription,
|
||||
FieldError,
|
||||
FieldLabel,
|
||||
Input,
|
||||
} from "@suaveplan/react-ui";
|
||||
import { useState } from "react";
|
||||
|
||||
export function PasswordField() {
|
||||
const [pwd, setPwd] = useState("");
|
||||
const tooShort = pwd.length > 0 && pwd.length < 8;
|
||||
|
||||
return (
|
||||
<Field invalid={tooShort}>
|
||||
<FieldLabel htmlFor="pwd">Password</FieldLabel>
|
||||
<Input
|
||||
id="pwd"
|
||||
type="password"
|
||||
value={pwd}
|
||||
invalid={tooShort}
|
||||
aria-invalid={tooShort}
|
||||
aria-describedby="pwd-help pwd-err"
|
||||
onChange={(e) => setPwd(e.currentTarget.value)}
|
||||
/>
|
||||
<FieldDescription id="pwd-help">
|
||||
At least 8 characters.
|
||||
</FieldDescription>
|
||||
{tooShort ? (
|
||||
<FieldError id="pwd-err">Too short.</FieldError>
|
||||
) : null}
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Field>`
|
||||
|
||||
`forwardRef<HTMLDivElement, FieldProps>` — renders a `<div>` container.
|
||||
|
||||
#### `FieldProps`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `invalid` | `boolean` | `false` | Drives `data-invalid="true"` on the container. |
|
||||
| `disabled` | `boolean` | `false` | Drives `data-disabled="true"` on the container. |
|
||||
| _all native_ `HTMLAttributes<HTMLDivElement>` | _native_ | _native_ | Forwarded as-is. |
|
||||
|
||||
### `<FieldLabel>`
|
||||
|
||||
`forwardRef<ComponentRef<typeof LabelPrimitive.Root>, FieldLabelProps>` — wraps `@radix-ui/react-label` with the chassis cva applied.
|
||||
|
||||
#### `FieldLabelProps`
|
||||
|
||||
Inherits every prop from `LabelPrimitive.Root` — `htmlFor`, `children`, native HTML attributes.
|
||||
|
||||
### `<FieldDescription>`
|
||||
|
||||
`forwardRef<HTMLParagraphElement, FieldDescriptionProps>` — renders a `<p>` with muted text.
|
||||
|
||||
#### `FieldDescriptionProps`
|
||||
|
||||
Inherits every prop from `HTMLAttributes<HTMLParagraphElement>`.
|
||||
|
||||
### `<FieldError>`
|
||||
|
||||
`forwardRef<HTMLParagraphElement, FieldErrorProps>` — renders a `<p>` with destructive colour and `role="alert"` by default.
|
||||
|
||||
#### `FieldErrorProps`
|
||||
|
||||
Inherits every prop from `HTMLAttributes<HTMLParagraphElement>`. The default `role="alert"` can be overridden by passing `role="status"` (for non-blocking errors).
|
||||
|
||||
#### Cva resolvers
|
||||
|
||||
`fieldVariants`, `fieldLabelVariants`, `fieldDescriptionVariants`, `fieldErrorVariants` are all exported as bare cva resolvers for external composition.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The four primitives are co-located in a single component module because they always compose together; splitting them into four directories would create import-tax for no benefit.
|
||||
- `<Field>` does NOT walk children to detect invalid/disabled state — consumers pass the booleans explicitly. This keeps the chassis a pure styling primitive.
|
||||
- The cva `group/field` named-group selector lets `<FieldLabel>` flip its colour without needing a sibling `<FieldError>` to be present.
|
||||
- `<FieldError>` defaults to `role="alert"` (assertive announcement) so screen readers announce validation errors immediately when they appear. Consumers that want a polite announcement override with `role="status"`.
|
||||
- Per the cross-cutting reconciliation note (#1), shadcn ships `field` and `field-group` as separate registry items; this chassis mirrors that split so the primitives evolve independently and can be ejected piecemeal.
|
||||
@@ -0,0 +1,78 @@
|
||||
# Field Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`fieldVariants`, `fieldLabelVariants`, `fieldDescriptionVariants`, and `fieldErrorVariants` are the four cva (class-variance-authority) configurations powering the `<Field>` primitives. They are split from `field.tsx` into their own module so an ejected source copy under `npx shadcn@latest add @suaveplan/field` keeps the cva configurations in a dedicated file. Theme overrides flow through `useThemedVariants("Field" | "FieldLabel" | "FieldDescription" | "FieldError")`.
|
||||
|
||||
The container cva uses Tailwind's named-group selector (`group/field`) so the descendant `<FieldLabel>` instances can react to `data-invalid="true"` on the container without depending on direct-parent semantics.
|
||||
|
||||
## Features
|
||||
|
||||
- Container `flex flex-col gap-2` layout (NOT `space-y-*` per shadcn skill rule).
|
||||
- `data-[invalid=true]:[&_label]:text-destructive` on the container — every descendant `<label>` (including nested `<FieldLabel>`) flips colour.
|
||||
- `data-[disabled=true]:opacity-70` decay.
|
||||
- `<FieldLabel>` cva inherits the destructive colour via `group-data-[invalid=true]/field:text-destructive`.
|
||||
- `<FieldDescription>` muted via `text-sm text-muted-foreground`.
|
||||
- `<FieldError>` destructive via `text-sm font-medium text-destructive`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
fieldVariants,
|
||||
fieldLabelVariants,
|
||||
fieldErrorVariants,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
const containerCls = fieldVariants();
|
||||
const labelCls = fieldLabelVariants();
|
||||
const errorCls = fieldErrorVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { fieldVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomField({
|
||||
invalid,
|
||||
children,
|
||||
}: {
|
||||
invalid: boolean;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
data-invalid={invalid ? "true" : undefined}
|
||||
className={cn(fieldVariants(), "rounded border p-2")}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `fieldVariants(): string`
|
||||
|
||||
Returns the container classes — `group/field flex flex-col gap-2 …`.
|
||||
|
||||
### `fieldLabelVariants(): string`
|
||||
|
||||
Returns the label classes including the `group-data-[invalid=true]/field:text-destructive` selector.
|
||||
|
||||
### `fieldDescriptionVariants(): string`
|
||||
|
||||
Returns the muted helper-text classes.
|
||||
|
||||
### `fieldErrorVariants(): string`
|
||||
|
||||
Returns the destructive error-text classes.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The named group `group/field` is named (not the bare `group` class) so nested `<Field>` instances do not collide — Tailwind treats `group/field` as a distinct selector from a parent `group/section`.
|
||||
- The container cva applies the destructive colour to ALL descendant `<label>` elements (`[&_label]:text-destructive`) so even a hand-rolled `<label>` inside `<Field>` picks up the colour without needing `<FieldLabel>`. This is intentional: consumers using the form library may render their own labels via `react-hook-form` field arrays.
|
||||
- Per the shadcn skill rule, the cva NEVER uses `space-y-*` / `space-x-*` — `gap-*` flows the same in RTL.
|
||||
- `<FieldError>` inherits the destructive colour directly (via `text-destructive`) rather than via the group selector so it always reads as an error even when `<Field invalid>` is not set (e.g. server-side error surfaces).
|
||||
128
docs/ui/react/react-ui/src/components/hover-card/hover-card.md
Normal file
128
docs/ui/react/react-ui/src/components/hover-card/hover-card.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# HoverCard
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - hover-card.variants — `hoverCardContentVariants` is…
|
||||
- [hover-card.variants](./hover-card.variants.md) — `hoverCardContentVariants` is the cva configuration powering the `<HoverCardContent>` floating panel. It is split from…
|
||||
- hover-card.variants — `hoverCardContentVariants` is…
|
||||
|
||||
- [hover-card.variants](./hover-card.variants.md) — `hoverCardContentVariants` is the cva configuration powering the `<HoverCardContent>` floating panel. It is split from…
|
||||
|
||||
- hover-card.variants — `hoverCardContentVariants` is…
|
||||
|
||||
- [hover-card.variants](./hover-card.variants.md) — `hoverCardContentVariants` is the cva configuration powering the `<HoverCardContent>` floating panel. It is split from…
|
||||
|
||||
- hover-card.variants — `hoverCardContentVariants` is…
|
||||
|
||||
- [hover-card.variants](./hover-card.variants.md) — `hoverCardContentVariants` is the cva configuration powering the `<HoverCardContent>` floating panel. It is split from…
|
||||
- [hover-card.variants](./hover-card.variants.md) — `hoverCardContentVariants` is the cva configuration powering the `<HoverCardContent>` floating panel. It is split from…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<HoverCard>` and its sub-components (`<HoverCardTrigger>`, `<HoverCardPortal>`, `<HoverCardContent>`, `<HoverCardArrow>`) are the chassis hover-card primitives built on `@radix-ui/react-hover-card`. Use this for non-modal previews that open on hover or focus over the trigger and dismiss on hover-out or blur — link previews, user-mention bios, contextual mini-cards.
|
||||
|
||||
Distinct from `<Popover>`: HoverCard is hover-driven (mouse-only by spec; touch devices fall back to tap-to-open) with `openDelay` / `closeDelay` hysteresis. Distinct from `<Tooltip>`: HoverCard panels can hold rich content (multiple paragraphs, images, action links); Tooltip is a single short label.
|
||||
|
||||
## Features
|
||||
|
||||
- Opens on hover or focus over the trigger; closes on hover-out or blur.
|
||||
- `openDelay` / `closeDelay` numeric hysteresis (default 700/300 ms — Radix defaults).
|
||||
- Side / align positional API (`side`: top/right/bottom/left; `align`: start/center/end).
|
||||
- Self-portals via `<HoverCardPortal>`.
|
||||
- Optional `<HoverCardArrow>` decoration.
|
||||
- Theme-driven cva via `useThemedVariants("HoverCardContent")`.
|
||||
- All sub-components forward refs.
|
||||
- `motion-reduce:animate-none` collapses the open/close animations under `prefers-reduced-motion: reduce`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
HoverCard,
|
||||
HoverCardContent,
|
||||
HoverCardTrigger,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function MentionPreview() {
|
||||
return (
|
||||
<HoverCard>
|
||||
<HoverCardTrigger>@suaveplan</HoverCardTrigger>
|
||||
<HoverCardContent>
|
||||
Component library for the Suaveplan platform.
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
HoverCard,
|
||||
HoverCardArrow,
|
||||
HoverCardContent,
|
||||
HoverCardTrigger,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
interface User {
|
||||
readonly handle: string;
|
||||
readonly displayName: string;
|
||||
readonly bio: string;
|
||||
readonly avatarUrl: string;
|
||||
}
|
||||
|
||||
export function UserMention({ user }: { user: User }) {
|
||||
return (
|
||||
<HoverCard openDelay={400} closeDelay={200}>
|
||||
<HoverCardTrigger asChild>
|
||||
<a href={`/${user.handle}`}>@{user.handle}</a>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent side="top" align="start" sideOffset={8}>
|
||||
<HoverCardArrow />
|
||||
<div className="flex gap-3">
|
||||
<img
|
||||
src={user.avatarUrl}
|
||||
alt=""
|
||||
className="h-10 w-10 rounded-full"
|
||||
/>
|
||||
<div>
|
||||
<h4 className="font-semibold">{user.displayName}</h4>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
@{user.handle}
|
||||
</p>
|
||||
<p className="mt-1 text-sm">{user.bio}</p>
|
||||
</div>
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<HoverCard>`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `open` | `boolean` | _uncontrolled_ | Controlled open state. |
|
||||
| `defaultOpen` | `boolean` | `false` | Initial open state. |
|
||||
| `onOpenChange` | `(open: boolean) => void` | _none_ | Fires on open/close transitions. |
|
||||
| `openDelay` | `number` | `700` | Delay (ms) before opening on hover. |
|
||||
| `closeDelay` | `number` | `300` | Delay (ms) before closing on hover-out. |
|
||||
|
||||
### `<HoverCardContent>`
|
||||
|
||||
Same positional props as `<PopoverContent>` (`side`, `align`, `sideOffset`, `alignOffset`, `avoidCollisions`, `collisionPadding`).
|
||||
|
||||
### `<HoverCardTrigger>`, `<HoverCardArrow>`, `<HoverCardPortal>`
|
||||
|
||||
Direct re-exports of the Radix sub-components.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis NEVER assigns a numeric `z-index` — Radix portals handle stacking.
|
||||
- HoverCard is hover-driven and therefore inappropriate for touch-only UI; consider `<Popover>` (click-driven) for tap-to-open ergonomics.
|
||||
- `<HoverCardContent>` self-portals via `<HoverCardPortal>` so consumers do not need to compose the portal manually.
|
||||
- `align="center"` and `sideOffset={4}` are chassis defaults (matching shadcn upstream).
|
||||
@@ -0,0 +1,52 @@
|
||||
# HoverCard Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`hoverCardContentVariants` is the cva configuration powering the `<HoverCardContent>` floating panel. It is split from `hover-card.tsx` into its own module so an ejected source copy under `npx shadcn@latest add @suaveplan/hover-card` keeps the cva configuration in a dedicated file. Theme overrides flow through `useThemedVariants("HoverCardContent")`.
|
||||
|
||||
The cva carries the visual recipe (rounded border, popover-foreground colours, shadow, fixed `w-64` width — narrower than `<PopoverContent>` because hover cards typically hold richer content but live shorter on screen) plus the same animation recipe as Popover.
|
||||
|
||||
## Features
|
||||
|
||||
- Fixed `w-64` width (slightly narrower than Popover's `w-72`).
|
||||
- `data-state` open/close fade + zoom animations.
|
||||
- `data-side` slide-in direction matching the resolved positioning.
|
||||
- `bg-popover text-popover-foreground` so the panel reads against any surface.
|
||||
- `motion-reduce:animate-none` collapses animations under reduced-motion preference.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { hoverCardContentVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const className = hoverCardContentVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { hoverCardContentVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomHoverCardShell() {
|
||||
return (
|
||||
<div
|
||||
data-state="open"
|
||||
data-side="top"
|
||||
className={cn(hoverCardContentVariants(), "w-80 p-6")}
|
||||
>
|
||||
…
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `hoverCardContentVariants(): string`
|
||||
|
||||
Returns the hover-card content classes; takes no arguments — the hover card is not parameterised by an axis.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Per the shadcn skill rule, the cva NEVER assigns a numeric `z-index`. Radix's `<Portal>` mounts the content above all page content via DOM placement.
|
||||
- Width is fixed at `w-64` to match the shadcn upstream recipe — consumers who need a wider panel layer their own width override via `cn(...)` at the call site.
|
||||
163
docs/ui/react/react-ui/src/components/input-group/input-group.md
Normal file
163
docs/ui/react/react-ui/src/components/input-group/input-group.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# InputGroup
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - input-group.variants — `inputGroupVariants`, `input…
|
||||
- [input-group.variants](./input-group.variants.md) — `inputGroupVariants`, `inputGroupAddonVariants`, `inputGroupInputVariants`, `inputGroupTextareaVariants`, and `inputGro…
|
||||
- input-group.variants — `inputGroupVariants`, `input…
|
||||
|
||||
- [input-group.variants](./input-group.variants.md) — `inputGroupVariants`, `inputGroupAddonVariants`, `inputGroupInputVariants`, `inputGroupTextareaVariants`, and `inputGro…
|
||||
|
||||
- input-group.variants — `inputGroupVariants`, `input…
|
||||
|
||||
- [input-group.variants](./input-group.variants.md) — `inputGroupVariants`, `inputGroupAddonVariants`, `inputGroupInputVariants`, `inputGroupTextareaVariants`, and `inputGro…
|
||||
|
||||
- input-group.variants — `inputGroupVariants`, `input…
|
||||
|
||||
- [input-group.variants](./input-group.variants.md) — `inputGroupVariants`, `inputGroupAddonVariants`, `inputGroupInputVariants`, `inputGroupTextareaVariants`, and `inputGro…
|
||||
- [input-group.variants](./input-group.variants.md) — `inputGroupVariants`, `inputGroupAddonVariants`, `inputGroupInputVariants`, `inputGroupTextareaVariants`, and `inputGro…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<InputGroup>` is the chassis container that wraps an input alongside leading/trailing addons (icons, prefix/suffix text, inline buttons). It hoists the focus ring and invalid border onto the wrapper itself so the inner control can drop its own ring; the dedicated `<InputGroupInput>` and `<InputGroupTextarea>` primitives strip the standard border / shadow / focus ring so the wrapper carries them.
|
||||
|
||||
Per the shadcn skill rule, NEVER drop a raw `<Input>` or `<Textarea>` inside `<InputGroup>` — the inner control needs the wrapped surface variants to remove its own border and focus ring. The chassis provides `<InputGroupInput>` and `<InputGroupTextarea>` for the input case. Inline buttons inside inputs use `<InputGroupAddon position="none">` + `<InputGroupButton>` (renders an icon-button without a separator border).
|
||||
|
||||
## Features
|
||||
|
||||
- Wrapper container shares focus ring + invalid border with every child via `focus-within:` and `data-[invalid=true]:`.
|
||||
- `<InputGroupAddon>` with three positions (`start` / `end` / `none`) controlling which separator border is drawn.
|
||||
- `<InputGroupInput>` strips the standard `<Input>` border / shadow / focus ring.
|
||||
- `<InputGroupTextarea>` strips the standard `<Textarea>` border / shadow / focus ring.
|
||||
- `<InputGroupButton>` for inline icon-buttons (toggle password visibility, clear input, attach file).
|
||||
- `placeholder` on `<InputGroupInput>` / `<InputGroupTextarea>` typed as `TranslationKey | undefined` and resolved via active i18n.
|
||||
- `data-invalid="true"` and `data-disabled="true"` on the wrapper drive cva selectors.
|
||||
- Refs forwarded on every primitive.
|
||||
- `motion-reduce:transition-none` collapses transitions for `prefers-reduced-motion: reduce`.
|
||||
- Theme-driven variant overrides via `useThemedVariants("InputGroup" | "InputGroupAddon" | "InputGroupInput" | "InputGroupTextarea" | "InputGroupButton")`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
InputGroup,
|
||||
InputGroupAddon,
|
||||
InputGroupInput,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function EmailField() {
|
||||
return (
|
||||
<InputGroup>
|
||||
<InputGroupAddon>@</InputGroupAddon>
|
||||
<InputGroupInput type="email" />
|
||||
</InputGroup>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
InputGroup,
|
||||
InputGroupAddon,
|
||||
InputGroupButton,
|
||||
InputGroupInput,
|
||||
} from "@suaveplan/react-ui";
|
||||
import { EyeIcon, EyeOffIcon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
export function PasswordWithReveal() {
|
||||
const [show, setShow] = useState(false);
|
||||
const [pwd, setPwd] = useState("");
|
||||
const tooShort = pwd.length > 0 && pwd.length < 8;
|
||||
|
||||
return (
|
||||
<InputGroup invalid={tooShort}>
|
||||
<InputGroupAddon>Pwd</InputGroupAddon>
|
||||
<InputGroupInput
|
||||
type={show ? "text" : "password"}
|
||||
value={pwd}
|
||||
aria-invalid={tooShort}
|
||||
onChange={(e) => setPwd(e.currentTarget.value)}
|
||||
/>
|
||||
<InputGroupAddon position="none">
|
||||
<InputGroupButton
|
||||
aria-label={show ? "Hide password" : "Show password"}
|
||||
onClick={() => setShow((s) => !s)}
|
||||
>
|
||||
{show ? <EyeOffIcon /> : <EyeIcon />}
|
||||
</InputGroupButton>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<InputGroup>`
|
||||
|
||||
`forwardRef<HTMLDivElement, InputGroupProps>` — renders a `<div>` wrapper.
|
||||
|
||||
#### `InputGroupProps`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `invalid` | `boolean` | `false` | Drives `data-invalid="true"` on the wrapper so border + focus ring flip to destructive. |
|
||||
| `disabled` | `boolean` | `false` | Drives `data-disabled="true"` for opacity decay. Pass to inner `<InputGroupInput>` separately for native semantics. |
|
||||
| _all native_ `HTMLAttributes<HTMLDivElement>` | _native_ | _native_ | Forwarded as-is. |
|
||||
|
||||
### `<InputGroupAddon>`
|
||||
|
||||
`forwardRef<HTMLDivElement, InputGroupAddonProps>` — renders a `<div>` for decorative content.
|
||||
|
||||
#### `InputGroupAddonProps`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `position` | `"start" \| "end" \| "none"` | `"start"` | Which side the addon hugs and which separator border is drawn (`none` = no separator, used for inline buttons). |
|
||||
|
||||
### `<InputGroupInput>`
|
||||
|
||||
`forwardRef<HTMLInputElement, InputGroupInputProps>` — renders an `<input>` with the standard border / shadow / focus ring stripped.
|
||||
|
||||
#### `InputGroupInputProps`
|
||||
|
||||
Inherits every prop from `InputHTMLAttributes<HTMLInputElement>` minus `size` / `placeholder`. Adds:
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `placeholder` | `TranslationKey` | _none_ | Resolved via active i18n. |
|
||||
|
||||
### `<InputGroupTextarea>`
|
||||
|
||||
`forwardRef<HTMLTextAreaElement, InputGroupTextareaProps>` — renders a `<textarea>` with the standard border / shadow / focus ring stripped.
|
||||
|
||||
#### `InputGroupTextareaProps`
|
||||
|
||||
Inherits every prop from `TextareaHTMLAttributes<HTMLTextAreaElement>` minus `placeholder`. Adds:
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `placeholder` | `TranslationKey` | _none_ | Resolved via active i18n. |
|
||||
|
||||
### `<InputGroupButton>`
|
||||
|
||||
`forwardRef<HTMLButtonElement, InputGroupButtonProps>` — renders a small icon-button with `type="button"` by default so it does not accidentally submit the surrounding form.
|
||||
|
||||
#### `InputGroupButtonProps`
|
||||
|
||||
Inherits every prop from `ButtonHTMLAttributes<HTMLButtonElement>`. The default `type="button"` can be overridden if intentional.
|
||||
|
||||
#### Cva resolvers
|
||||
|
||||
`inputGroupVariants`, `inputGroupAddonVariants`, `inputGroupInputVariants`, `inputGroupTextareaVariants`, and `inputGroupButtonVariants` are all exported as bare cva resolvers for external composition.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Per the shadcn skill rule, the chassis enforces the `<InputGroupInput>` / `<InputGroupTextarea>` wrappers — raw `<Input>` / `<Textarea>` inside `<InputGroup>` carries an inner border + focus ring that double up with the wrapper's own.
|
||||
- The `position="none"` value on `<InputGroupAddon>` is the supported way to render an inline button without a separator border. The button supplies its own padding so the addon does not need one.
|
||||
- `<InputGroupButton>` defaults to `type="button"` so a button rendered inside an input group does not submit the surrounding form when clicked. Override with `type="submit"` only when intentional.
|
||||
- The wrapper carries the focus ring via `focus-within:` so any inner element gaining focus surfaces the ring on the wrapper. The inner `<InputGroupInput>` removes its own ring via `focus:ring-0` to avoid the visual double-ring.
|
||||
- Refs target the underlying DOM element on every primitive — consumers can `.focus()` the input, button, addon, or wrapper programmatically.
|
||||
@@ -0,0 +1,80 @@
|
||||
# InputGroup Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`inputGroupVariants`, `inputGroupAddonVariants`, `inputGroupInputVariants`, `inputGroupTextareaVariants`, and `inputGroupButtonVariants` are the five cva (class-variance-authority) configurations powering the `<InputGroup>` primitives. They are split from `input-group.tsx` into their own module so an ejected source copy under `npx shadcn@latest add @suaveplan/input-group` keeps the cva configurations in a dedicated file. Theme overrides flow through `useThemedVariants(...)`.
|
||||
|
||||
The wrapper cva carries the shared border + focus ring; the inner `<InputGroupInput>` / `<InputGroupTextarea>` cvas strip the standard border + shadow + ring that would otherwise double up with the wrapper's own.
|
||||
|
||||
## Features
|
||||
|
||||
- Wrapper `flex w-full items-center rounded-md border border-input bg-transparent shadow-sm` with `focus-within:ring-2` and `data-[invalid=true]:border-destructive`.
|
||||
- Addon recipe with three positions: `start` (right-border separator), `end` (left-border separator), `none` (no separator, used for inline buttons).
|
||||
- Input recipe strips the standard `<Input>` border / shadow / focus ring (`border-0 outline-none shadow-none focus:ring-0`).
|
||||
- Textarea recipe strips the standard `<Textarea>` border / shadow / focus ring with a default `min-height: 80px`.
|
||||
- Button recipe carries the inline icon-button surface (`h-8 w-8 mx-1 rounded-md`) with the standard `data-icon` slot rules.
|
||||
- NEVER uses `space-x-*` per the shadcn skill rule.
|
||||
- `motion-reduce:transition-none` for users with `prefers-reduced-motion: reduce`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
inputGroupVariants,
|
||||
inputGroupInputVariants,
|
||||
inputGroupAddonVariants,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
const wrapperCls = inputGroupVariants();
|
||||
const inputCls = inputGroupInputVariants();
|
||||
const addonCls = inputGroupAddonVariants({ position: "start" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
inputGroupVariants,
|
||||
inputGroupInputVariants,
|
||||
cn,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomInputGroup() {
|
||||
return (
|
||||
<div className={cn(inputGroupVariants(), "ring-2 ring-accent")}>
|
||||
<span className="px-3 text-sm text-muted-foreground">@</span>
|
||||
<input className={inputGroupInputVariants()} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `inputGroupVariants(): string`
|
||||
|
||||
Returns the wrapper classes.
|
||||
|
||||
### `inputGroupAddonVariants(props?: { position?: "start" | "end" | "none" }): string`
|
||||
|
||||
Returns the addon classes. Omitted position falls back to the cva `defaultVariants` (`position: "start"`).
|
||||
|
||||
### `inputGroupInputVariants(): string`
|
||||
|
||||
Returns the input classes — strips inner border / shadow / focus ring so the wrapper carries them.
|
||||
|
||||
### `inputGroupTextareaVariants(): string`
|
||||
|
||||
Returns the textarea classes — same border-stripping treatment as the input.
|
||||
|
||||
### `inputGroupButtonVariants(): string`
|
||||
|
||||
Returns the inline icon-button classes — `h-8 w-8 mx-1 rounded-md` with the standard `data-icon` slot rules.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The five cvas are kept separate so consumer themes can override each independently — for example a theme that styles the wrapper as a pill while leaving the inner input untouched.
|
||||
- The input's `focus:ring-0 focus-visible:ring-0` is intentional: the wrapper carries the ring via `focus-within:ring-2` so removing the inner ring is the only way to avoid the double-ring artefact. Consumers MUST use `<InputGroupInput>` (not `<Input>`) inside the group for this reason.
|
||||
- The button cva uses `mx-1` so the inline button does not press flush against the input's text — a minor padding tweak that keeps the layout breathing under both LTR and RTL.
|
||||
- Per the shadcn skill rule, the cvas NEVER use `space-x-*` / `space-y-*`.
|
||||
- Tokens reference theme-driven CSS custom properties (`border-input`, `bg-transparent`, `text-muted-foreground`) so the colour palette comes from the active `ThemePlugin.cssVariables` payload.
|
||||
@@ -0,0 +1,80 @@
|
||||
# `<InputNumber>`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Locale-aware numeric input. Parses incoming text using the active locale's decimal and grouping separators, accepts locale-specific digit glyphs (`٣٫١٤` in Arabic-Indic), and emits a canonical `number | null` through `onValueChange`. Switches to a raw editable buffer on focus to avoid cursor jumps caused by re-formatting mid-keystroke; renders the locale-formatted value on blur. The plain `<Input>` is unaffected — `<InputNumber>` is a sibling primitive consumers opt into per call site, so existing form code is not disturbed.
|
||||
|
||||
## Features
|
||||
|
||||
- Per-locale separator detection via `Intl.NumberFormat.formatToParts(12345.6)` — handles en-GB `1,234.5`, de-DE `1.234,5`, fr-FR `1 234,5`, ar-AE Arabic-Indic glyphs.
|
||||
- Digit-folding for non-Latin numbering systems — Eastern Arabic, Devanagari, Bengali, etc.
|
||||
- Focus / blur buffer split — raw Latin during edit, locale formatted at rest.
|
||||
- Empty input emits `null` (not `0` or `NaN`).
|
||||
- Invalid input on blur reverts to the previously valid formatted value rather than corrupting state.
|
||||
- Controlled OR uncontrolled — pass `value` (controlled) or `defaultValue` (uncontrolled).
|
||||
- `displayOptions: Intl.NumberFormatOptions` lets the rest-state format render percent, currency, scientific, etc.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { InputNumber } from "@suaveplan/react-ui";
|
||||
|
||||
const [amount, setAmount] = useState<number | null>(null);
|
||||
|
||||
<InputNumber value={amount} onValueChange={setAmount} />
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
Currency-style display (rest-state shows `1,234.50` while focused buffer shows `1234.5`):
|
||||
|
||||
```tsx
|
||||
<InputNumber
|
||||
value={price}
|
||||
onValueChange={setPrice}
|
||||
displayOptions={{
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
}}
|
||||
inputMode="decimal"
|
||||
aria-label="Unit price"
|
||||
/>
|
||||
```
|
||||
|
||||
Bind directly to a form library's controlled value, with locale override for a multi-currency entry form:
|
||||
|
||||
```tsx
|
||||
<InputNumber
|
||||
locale="de-DE"
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<InputNumber value? defaultValue? onValueChange? locale? displayOptions? {...rest}>`
|
||||
|
||||
- `value?: number | null` — controlled. `null` renders empty.
|
||||
- `defaultValue?: number | null` — uncontrolled initial.
|
||||
- `onValueChange?: (value: number | null) => void` — fires on blur.
|
||||
- `locale?: string` — defaults to `useReactUiLocale().locale`.
|
||||
- `displayOptions?: Intl.NumberFormatOptions`.
|
||||
- All other `<input>` HTML attributes are forwarded EXCEPT `value`, `defaultValue`, `onChange`, `type` (the component owns those).
|
||||
|
||||
### `parseLocaleNumber(text, locale): number | null`
|
||||
|
||||
Standalone utility for parsing a locale-formatted numeric string. Used internally by the component; exported for consumers needing the same parse logic outside the component.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The `Intl.NumberFormat` instance is cached per `(locale, displayOptions)` via `useMemo`. The separator probe (`formatToParts(12345.6)`) is also cached per locale at module level.
|
||||
- `onValueChange` fires only on blur to avoid emitting `null` for every transient unparseable keystroke. Forms that need keystroke-level validation can subscribe to `onChange` on the underlying `<input>` directly via the spread (we do not own `onChange` once the user has supplied one).
|
||||
- The component uses `type="text"` with `inputMode="decimal"` rather than `type="number"`. `type="number"` rejects locale-specific separators in Chrome and triggers spinner UI consumers usually do not want.
|
||||
- When a parent re-renders with an externally-changed `value` while the input is NOT focused, the displayed value re-formats. While focused, the focused-buffer text wins so the user is not interrupted mid-edit.
|
||||
117
docs/ui/react/react-ui/src/components/input/input.md
Normal file
117
docs/ui/react/react-ui/src/components/input/input.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# Input
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
- [input-number](./input-number/input-number.md) — <!-- begin:auto-docs --> _(no submodules)_- [input](./input.md) — <!-- begin:auto-docs --> _(no submodules)_- input-number — <!-- begin:auto-docs --> _(no submodu…
|
||||
- [input.variants](./input.variants.md) — `inputVariants` is the cva (class-variance-authority) configuration that powers every `<Input>` render. It is split fro…
|
||||
- [input-number](./input-number/input-number.md) — <!-- begin:auto-docs --> _(no submodules)_- [input](./input.md) — <!-- begin:auto-docs --> _(no submodules)_- [input.variants](./input.variants.md) — `inputVariants` is the cva (class-variance-authority) configuration that powers every `<Input>` render. It is split fro…
|
||||
- [input-number](./input-number/input-number.md) — <!-- begin:auto-docs --> _(no submodules)_- [input](./input.md) — <!-- begin:auto-docs --> _(no submodules)_- input-number — <!-- begin:au…
|
||||
- [input.variants](./input.variants.md) — `inputVariants` is the cva (class-variance-authority) configuration that powers every `<Input>` render. It is split fro…
|
||||
- [input-number](./input-number/input-number.md) — <!-- begin:auto-docs --> _(no submodules)_- [input](./input.md) — <!-- begin:auto-docs --> _(no submodules)_- input-number — <!-- begin:auto-docs --> _(no submod…
|
||||
- [input.variants](./input.variants.md) — `inputVariants` is the cva (class-variance-authority) configuration that powers every `<Input>` render. It is split fro…
|
||||
- [input-number](./input-number/input-number.md) — <!-- begin:auto-docs --> _(no submodules)_- [input](./input.md) — <!-- begin:auto-docs --> _(no submodules)_- [input.variants](./input.variants.md) — `inputVariants` is the cva (class-variance-authority) configuration that powers every `<Input>` render. It is split fro…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Input>` is the chassis primitive form input for `@suaveplan/react-ui`. It renders a native `<input>` element with theme-driven variant resolution, forwards refs to the underlying DOM node, and threads its `placeholder` through the active i18n instance so consumers can never accidentally ship a hardcoded English placeholder. The component is intentionally thin — the shadcn upstream Input recipe is structurally simple and the chassis follows suit, exposing a single `default` variant + `default` size at the cva level so themes carry the visual variation rather than forcing the component to enumerate it.
|
||||
|
||||
`<Input>` participates in the same `data-*` attribute convention as the rest of `@suaveplan/react-ui`: `data-invalid="true"` and `data-disabled="true"` are written when the corresponding props are set, so theme overrides can target error state via a single CSS selector without depending on `aria-invalid` (which the consumer may or may not have set independently).
|
||||
|
||||
## Features
|
||||
|
||||
- Forwards refs to the underlying `<input>` so consumers can focus, select, measure, or animate it directly.
|
||||
- All native `InputHTMLAttributes` are forwarded as-is (`onChange`, `value`, `defaultValue`, `name`, `autoComplete`, `inputMode`, `pattern`, etc.).
|
||||
- `placeholder` typed as `TranslationKey | undefined` — the chassis enforces that the placeholder text is a registered translation key, never a raw English literal. Passing `undefined` skips the attribute entirely.
|
||||
- `invalid` boolean prop drives `data-invalid="true"` on the rendered element so the cva config's `data-[invalid=true]:border-destructive` rule activates.
|
||||
- `disabled` mirrors as `data-disabled="true"` for the parity with shadcn's selector convention.
|
||||
- Theme variant overrides via `useThemedVariants("Input", inputVariants)`. A `ThemePlugin.variantOverrides.Input` may add brand-new size or appearance variants (see the type-safety trade-off in `proposal.md` §"Type-safety trade-off").
|
||||
- `motion-reduce:transition-none` collapses focus/border transitions for users with `prefers-reduced-motion: reduce`.
|
||||
- Focus styling via `focus-visible:ring-2 focus-visible:ring-ring` — keyboard users get a visible ring; pointer focus does not.
|
||||
- File-input styling via `file:border-0 file:bg-transparent file:text-sm file:font-medium` so `<Input type="file">` inherits a sensible button look.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { Input } from "@suaveplan/react-ui";
|
||||
|
||||
export function NameField({
|
||||
onChange,
|
||||
}: {
|
||||
onChange: (next: string) => void;
|
||||
}) {
|
||||
return (
|
||||
<Input
|
||||
name="full-name"
|
||||
autoComplete="name"
|
||||
onChange={(event) => onChange(event.currentTarget.value)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { Input } from "@suaveplan/react-ui";
|
||||
|
||||
export function EmailField({
|
||||
value,
|
||||
error,
|
||||
onChange,
|
||||
}: {
|
||||
value: string;
|
||||
error: string | undefined;
|
||||
onChange: (next: string) => void;
|
||||
}) {
|
||||
return (
|
||||
<Input
|
||||
type="email"
|
||||
name="email"
|
||||
autoComplete="email"
|
||||
inputMode="email"
|
||||
placeholder="react-ui.empty.noResults"
|
||||
value={value}
|
||||
invalid={error !== undefined}
|
||||
aria-invalid={error !== undefined}
|
||||
aria-describedby={error !== undefined ? "email-error" : undefined}
|
||||
onChange={(event) => onChange(event.currentTarget.value)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Input>`
|
||||
|
||||
`forwardRef<HTMLInputElement, InputProps>` — renders a `<input type={type}>` (default `"text"`).
|
||||
|
||||
#### `InputProps`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `variant` | `"default"` | `"default"` | Visual variant. The chassis ships only `default`; themes may extend. |
|
||||
| `size` | `"default"` | `"default"` | Sizing variant. The chassis ships only `default`; themes may extend. |
|
||||
| `placeholder` | `TranslationKey \| undefined` | `undefined` | Translation key resolved against the active i18n instance. Hardcoded literals are rejected at compile time. |
|
||||
| `invalid` | `boolean` | `undefined` | When `true`, writes `data-invalid="true"` on the element so the cva error rules activate. |
|
||||
| `disabled` | `boolean` | `undefined` | Forwarded to the native `<input>` AND mirrored as `data-disabled="true"`. |
|
||||
| `type` | _native_ | `"text"` | Native input type. |
|
||||
| _all native_ `InputHTMLAttributes<HTMLInputElement>` | _native_ | _native_ | Forwarded as-is. |
|
||||
|
||||
#### `inputVariants`
|
||||
|
||||
The bare cva resolver. Exported so external consumers (`@suaveplan/react-ui-theme-*`, ejected source under `npx shadcn@latest add @suaveplan/input`) can re-use the variants directly without going through `useThemedVariants`.
|
||||
|
||||
#### Errors
|
||||
|
||||
- `useTranslation()` is called unconditionally inside the component. When the surrounding tree lacks a `<TranslationProvider>` (or `<ReactUiProvider>`), the hook returns its identity-fallback shape — `t(key)` returns the key as the literal. This matches the `react-i18n` "no-throw in production" contract; the component renders, but the rendered placeholder is the raw key.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The variants module is intentionally separate (`input.variants.ts`) so an ejected source copy keeps the cva config in a dedicated file, mirroring the upstream shadcn convention.
|
||||
- `useThemedVariants("Input", inputVariants)` is called unconditionally so the hook order is stable across renders. The hook's memoisation key is `(themeName, "Input")` so a re-render with the same theme returns the same resolver reference.
|
||||
- `placeholder` is omitted entirely when `undefined`, NOT set to the empty string. An empty placeholder attribute would still allocate the placeholder pseudo-element in the user-agent stylesheet and could collide with screen-reader heuristics.
|
||||
- `data-invalid` and `data-disabled` are written when `true`, omitted entirely when `false` or `undefined`. The CVA selector convention is `data-[invalid=true]:…` so an explicit `data-invalid="false"` would not match, but it would clutter the rendered HTML.
|
||||
- The chassis does NOT inject any `aria-invalid` attribute. Consumers who want `aria-invalid="true"` MUST pass it themselves alongside `invalid`. This separation lets the component represent purely visual error state (e.g. "the user has not yet attempted to submit, but we want to indicate the field is required").
|
||||
- `<Input type="file">` inherits the file-input styling rules from the cva base; consumers do not need to layer additional classes for the upload button.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Input Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`inputVariants` is the cva (class-variance-authority) configuration that powers every `<Input>` render. It is split from `input.tsx` into its own module so an ejected source copy under `npx shadcn@latest add @suaveplan/input` keeps the cva configuration in a dedicated file — exactly mirroring the upstream shadcn convention. Theme overrides flow through `useThemedVariants("Input", inputVariants)`, so the runtime cva resolver can be replaced or extended by an active `ThemePlugin` without touching the component source.
|
||||
|
||||
The module exports a single `inputVariants` callable resolver (`(props?) => string`) tagged with the component name `"Input"` via `defineVariants`. Consumers that want the bare resolver — for example to render the same variants on a non-`<input>` element — import `inputVariants` directly and apply the result via `cn()` themselves.
|
||||
|
||||
## Features
|
||||
|
||||
- Single `default` cva variant + single `default` size — the shadcn upstream Input recipe is structurally simple, and the chassis follows suit so themes carry the visual variation rather than forcing the component to enumerate it.
|
||||
- `data-[invalid=true]:border-destructive` + `data-[invalid=true]:focus-visible:ring-destructive` so the error state styling activates when the component writes `data-invalid="true"`.
|
||||
- `placeholder:text-muted-foreground` so the placeholder colour comes from the active theme's muted-foreground token.
|
||||
- `focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2` for keyboard-only focus styling.
|
||||
- `disabled:cursor-not-allowed disabled:opacity-50` for the native `disabled` state.
|
||||
- `file:border-0 file:bg-transparent file:text-sm file:font-medium` so `<Input type="file">` inherits a sensible button look without extra classes.
|
||||
- `motion-reduce:transition-none` so reduced-motion users see no border / focus transitions.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { inputVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const className = inputVariants({ variant: "default", size: "default" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { inputVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomTextarea(props: React.TextareaHTMLAttributes<HTMLTextAreaElement>) {
|
||||
return (
|
||||
<textarea
|
||||
{...props}
|
||||
className={cn(inputVariants({}), "h-32 resize-y", props.className)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `inputVariants(props?: VariantProps<typeof inputVariants>): string`
|
||||
|
||||
Returns the cva-merged class string for the supplied variant + size combination. Both fields are optional; omitted fields fall back to the cva `defaultVariants` (`variant: "default"`, `size: "default"`).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The resolver is tagged with `componentName: "Input"` (non-enumerable, non-writable) via `defineVariants` so debug tooling can identify the cva instance at runtime.
|
||||
- The single-variant / single-size shape is deliberate — sizing variation typically belongs to a wrapping `<FieldGroup>` / `<InputGroup>` (Wave 3b) rather than the input itself.
|
||||
- Tokens reference theme-driven CSS custom properties (`border-input`, `bg-transparent`, `text-muted-foreground`, `border-destructive`) — the actual colour values come from the active `ThemePlugin.cssVariables` payload.
|
||||
110
docs/ui/react/react-ui/src/components/label/label.md
Normal file
110
docs/ui/react/react-ui/src/components/label/label.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# Label
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - label.variants — `labelVariants` is the cva (class-…
|
||||
- [label.variants](./label.variants.md) — `labelVariants` is the cva (class-variance-authority) configuration that powers every `<Label>` render. It is split fro…
|
||||
- label.variants — `labelVariants` is the cva (class-…
|
||||
|
||||
- [label.variants](./label.variants.md) — `labelVariants` is the cva (class-variance-authority) configuration that powers every `<Label>` render. It is split fro…
|
||||
|
||||
- label.variants — `labelVariants` is the cva (class-…
|
||||
|
||||
- [label.variants](./label.variants.md) — `labelVariants` is the cva (class-variance-authority) configuration that powers every `<Label>` render. It is split fro…
|
||||
|
||||
- label.variants — `labelVariants` is the cva (class-…
|
||||
|
||||
- [label.variants](./label.variants.md) — `labelVariants` is the cva (class-variance-authority) configuration that powers every `<Label>` render. It is split fro…
|
||||
- [label.variants](./label.variants.md) — `labelVariants` is the cva (class-variance-authority) configuration that powers every `<Label>` render. It is split fro…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Label>` is the chassis form-label primitive for `@suaveplan/react-ui`. It wraps `@radix-ui/react-label` so consumers inherit the Radix labelling contract (a click on the label focuses the matched input via `htmlFor`, and synthetic click events originating inside the label are not double-fired on the input) while threading variant resolution through `useThemedVariants("Label")` and surfacing a `data-disabled="true"` mirror so visual disabled state can be represented without needing a sibling form control.
|
||||
|
||||
The label children are deliberately NOT translated by the chassis. Labels routinely interleave dynamic inline elements (`<strong>`, `<span>`, `<Trans>`, count badges, asterisks for required fields) and forcing the children through a single string-key lookup would prevent that composition. Consumers compose translation calls in their own JSX, e.g. `<Label htmlFor="email">{t('app.email.label')}</Label>` or `<Label htmlFor="email"><Trans i18nKey="app.email.label" /></Label>`.
|
||||
|
||||
## Features
|
||||
|
||||
- Wraps `@radix-ui/react-label` for full Radix labelling semantics (focus management on click, no double-fire of synthetic events).
|
||||
- Forwards refs to the underlying `<label>` element.
|
||||
- All native `LabelHTMLAttributes` are forwarded as-is via the Radix primitive's props passthrough.
|
||||
- `disabled` boolean prop writes `data-disabled="true"` on the rendered label so the cva config's `data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-70` rules activate.
|
||||
- `peer-disabled:` Tailwind variant baked into the cva base so a label adjacent to a `peer`-classed input picks up the disabled styling automatically when the input is disabled.
|
||||
- Theme variant overrides via `useThemedVariants("Label", labelVariants)`. A `ThemePlugin.variantOverrides.Label` may add brand-new variants (e.g. `variant: "section-heading"`).
|
||||
- No translation lookup on children — composition is consumer-driven.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { Label, Input } from "@suaveplan/react-ui";
|
||||
|
||||
export function EmailField() {
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input id="email" type="email" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { Label, Input } from "@suaveplan/react-ui";
|
||||
import { useTranslation } from "@suaveplan/react-i18n";
|
||||
|
||||
export function PasswordField({ disabled }: { disabled: boolean }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
<Label htmlFor="password" disabled={disabled}>
|
||||
{t("app.password.label")}{" "}
|
||||
<span aria-hidden="true" className="text-destructive">
|
||||
*
|
||||
</span>
|
||||
</Label>
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
disabled={disabled}
|
||||
className="peer"
|
||||
autoComplete="current-password"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Label>`
|
||||
|
||||
`forwardRef<HTMLLabelElement, LabelProps>` — renders a Radix `<Label.Root>` element (which itself renders a native `<label>` with the labelling enhancements applied).
|
||||
|
||||
#### `LabelProps`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `htmlFor` | `string` | _none_ | Required. The id of the form control this label is bound to. |
|
||||
| `variant` | `"default"` | `"default"` | Visual variant; the chassis ships only `default` so themes carry the variation. |
|
||||
| `size` | `"default"` | `"default"` | Sizing variant; the chassis ships only `default` so themes carry the variation. |
|
||||
| `disabled` | `boolean` | `undefined` | When `true`, writes `data-disabled="true"` so the cva opacity decay activates. |
|
||||
| _all_ `ComponentPropsWithoutRef<typeof LabelPrimitive.Root>` | _native_ | _native_ | Forwarded as-is to Radix. |
|
||||
|
||||
#### `labelVariants`
|
||||
|
||||
The bare cva resolver. Exported so external consumers (`@suaveplan/react-ui-theme-*`, ejected source under `npx shadcn@latest add @suaveplan/label`) can re-use the variants directly without going through `useThemedVariants`.
|
||||
|
||||
#### Errors
|
||||
|
||||
The Label component does NOT call `useTranslation()` so it does not interact with the i18n provider at all. Consumers that wrap `<Trans>` or `useTranslation` themselves inherit the upstream provider's no-throw-in-production contract.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis re-exports Radix's labelling semantics rather than re-implementing them: the click-on-label-focuses-input + click-event-bubble-suppression behaviour is non-trivial to get right, and Radix is the upstream-of-record.
|
||||
- `data-disabled="true"` is written when the prop is `true`, omitted when `false`/`undefined`. The CVA selector convention is `data-[disabled=true]:…`, so an explicit `data-disabled="false"` would not match but it would clutter the DOM.
|
||||
- The `peer-disabled:cursor-not-allowed peer-disabled:opacity-70` rules in the cva base let consumers wire a label adjacent to a `peer`-classed input — when the input is disabled, the label picks up the visual decay automatically without requiring the consumer to thread the `disabled` prop through both elements.
|
||||
- The variants module is intentionally separate (`label.variants.ts`) so an ejected source copy keeps the cva config in a dedicated file, mirroring the upstream shadcn convention.
|
||||
- `useThemedVariants("Label", labelVariants)` is called unconditionally so the hook order is stable across renders. The hook's memoisation key is `(themeName, "Label")` so a re-render with the same theme returns the same resolver reference.
|
||||
- Refs forward to the underlying `<label>` (Radix `Label.Root` is implemented as a `<label>` element, not a custom-element wrapper).
|
||||
@@ -0,0 +1,48 @@
|
||||
# Label Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`labelVariants` is the cva (class-variance-authority) configuration that powers every `<Label>` render. It is split from `label.tsx` into its own module so an ejected source copy under `npx shadcn@latest add @suaveplan/label` keeps the cva configuration in a dedicated file — exactly mirroring the upstream shadcn convention. Theme overrides flow through `useThemedVariants("Label", labelVariants)`, so the runtime cva resolver can be replaced or extended by an active `ThemePlugin` without touching the component source.
|
||||
|
||||
The module exports a single `labelVariants` callable resolver (`(props?) => string`) tagged with the component name `"Label"` via `defineVariants`. Consumers that want the bare resolver — for example to render the same label styling on a non-`<label>` element such as `<legend>` — import `labelVariants` directly and apply the result via `cn()` themselves.
|
||||
|
||||
## Features
|
||||
|
||||
- Single `default` cva variant + single `default` size — the shadcn upstream Label recipe is structurally simple; the chassis follows suit so themes carry the variation.
|
||||
- `peer-disabled:cursor-not-allowed peer-disabled:opacity-70` so a label adjacent to a `peer`-classed disabled input automatically picks up the visual decay without consumer plumbing.
|
||||
- `data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-70` so the chassis-supplied `disabled` prop drives the same visual decay even without a sibling input.
|
||||
- `text-sm font-medium leading-none` for the chassis baseline label typography.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { labelVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const className = labelVariants({ variant: "default", size: "default" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { labelVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function FieldsetLegend({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<legend className={cn(labelVariants({}), "px-2 text-base")}>
|
||||
{children}
|
||||
</legend>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `labelVariants(props?: VariantProps<typeof labelVariants>): string`
|
||||
|
||||
Returns the cva-merged class string for the supplied variant + size combination. Both fields are optional; omitted fields fall back to the cva `defaultVariants` (`variant: "default"`, `size: "default"`).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The resolver is tagged with `componentName: "Label"` (non-enumerable, non-writable) via `defineVariants` so debug tooling can identify the cva instance at runtime.
|
||||
- The chassis prefers the `peer-disabled` pattern over the chassis `disabled` prop when wiring labels next to inputs — passing `disabled` to both elements is redundant and risks drift between visual and functional state.
|
||||
- The cva base is intentionally minimal — labels routinely interleave dynamic inline elements (asterisks for required fields, badge counts, helper-text indicators) and the cva config does not attempt to style any of those; consumer composition owns them.
|
||||
207
docs/ui/react/react-ui/src/components/menubar/menubar.md
Normal file
207
docs/ui/react/react-ui/src/components/menubar/menubar.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# Menubar
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - menubar.variants — `menubarVariants`, `menubarTrigg…
|
||||
- [menubar.variants](./menubar.variants.md) — `menubarVariants`, `menubarTriggerVariants`, `menubarContentVariants`, `menubarItemVariants`, `menubarLabelVariants`, `…
|
||||
- menubar.variants — `menubarVariants`, `menubarTrigg…
|
||||
|
||||
- [menubar.variants](./menubar.variants.md) — `menubarVariants`, `menubarTriggerVariants`, `menubarContentVariants`, `menubarItemVariants`, `menubarLabelVariants`, `…
|
||||
|
||||
- menubar.variants — `menubarVariants`, `menubarTrigg…
|
||||
|
||||
- [menubar.variants](./menubar.variants.md) — `menubarVariants`, `menubarTriggerVariants`, `menubarContentVariants`, `menubarItemVariants`, `menubarLabelVariants`, `…
|
||||
|
||||
- menubar.variants — `menubarVariants`, `menubarTrigg…
|
||||
|
||||
- [menubar.variants](./menubar.variants.md) — `menubarVariants`, `menubarTriggerVariants`, `menubarContentVariants`, `menubarItemVariants`, `menubarLabelVariants`, `…
|
||||
- [menubar.variants](./menubar.variants.md) — `menubarVariants`, `menubarTriggerVariants`, `menubarContentVariants`, `menubarItemVariants`, `menubarLabelVariants`, `…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Menubar>` and its sub-components (`<MenubarMenu>`, `<MenubarTrigger>`, `<MenubarContent>`, `<MenubarItem>`, `<MenubarGroup>`, `<MenubarLabel>`, `<MenubarSeparator>`, `<MenubarShortcut>`, `<MenubarCheckboxItem>`, `<MenubarRadioGroup>`, `<MenubarRadioItem>`, `<MenubarSub>`, `<MenubarSubTrigger>`, `<MenubarSubContent>`, `<MenubarPortal>`) are the chassis menubar primitive built on `@radix-ui/react-menubar`. Radix supplies the WAI-ARIA menubar semantics (`role="menubar"`, `role="menu"`, arrow-key navigation across menus and within menus, type-ahead search, focus-trap while a menu is open, restore focus on close), and the chassis layers cva slots and theme overrides on top. Use when an application needs a desktop-style menu bar (File / Edit / View / etc.) on top of the page; use `<DropdownMenu>` instead for a single contextual menu.
|
||||
|
||||
**Composition rule (shadcn skill rule):** every top-level menu MUST be wrapped in `<MenubarMenu>`. Items in groups should sit inside a `<MenubarGroup>` so screen-readers announce them as one set. The `<MenubarShortcut>` displayed glyph is always consumer-supplied — the chassis NEVER hardcodes shortcut text.
|
||||
|
||||
## Features
|
||||
|
||||
- WAI-ARIA menubar semantics from Radix.
|
||||
- Arrow-key navigation across triggers (`ArrowLeft` / `ArrowRight`) and within menus (`ArrowUp` / `ArrowDown`).
|
||||
- Type-ahead search jumps to the next item starting with the typed letter.
|
||||
- Escape closes the menu and restores focus to the trigger.
|
||||
- Auto-portalled `<MenubarContent>` (matches shadcn upstream — consumers do not need to wrap in `<MenubarPortal>` manually).
|
||||
- `<MenubarShortcut>` accepts consumer-supplied glyph text — no hardcoded shortcut strings.
|
||||
- `<MenubarCheckboxItem>` / `<MenubarRadioGroup>` / `<MenubarRadioItem>` for stateful menu items.
|
||||
- `<MenubarSub>` for nested sub-menus with chevron indicator (RTL-flipped via `rtl:rotate-180`).
|
||||
- `inset` variant on `<MenubarItem>` / `<MenubarLabel>` / `<MenubarSubTrigger>` aligns with checkbox/radio rows.
|
||||
- All sub-components forward refs to their underlying Radix elements.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Menubar,
|
||||
MenubarContent,
|
||||
MenubarItem,
|
||||
MenubarMenu,
|
||||
MenubarTrigger,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function AppMenubar() {
|
||||
return (
|
||||
<Menubar>
|
||||
<MenubarMenu>
|
||||
<MenubarTrigger>File</MenubarTrigger>
|
||||
<MenubarContent>
|
||||
<MenubarItem>New</MenubarItem>
|
||||
<MenubarItem>Open</MenubarItem>
|
||||
<MenubarItem>Save</MenubarItem>
|
||||
</MenubarContent>
|
||||
</MenubarMenu>
|
||||
<MenubarMenu>
|
||||
<MenubarTrigger>Edit</MenubarTrigger>
|
||||
<MenubarContent>
|
||||
<MenubarItem>Cut</MenubarItem>
|
||||
<MenubarItem>Copy</MenubarItem>
|
||||
<MenubarItem>Paste</MenubarItem>
|
||||
</MenubarContent>
|
||||
</MenubarMenu>
|
||||
</Menubar>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Menubar,
|
||||
MenubarCheckboxItem,
|
||||
MenubarContent,
|
||||
MenubarGroup,
|
||||
MenubarItem,
|
||||
MenubarLabel,
|
||||
MenubarMenu,
|
||||
MenubarRadioGroup,
|
||||
MenubarRadioItem,
|
||||
MenubarSeparator,
|
||||
MenubarShortcut,
|
||||
MenubarSub,
|
||||
MenubarSubContent,
|
||||
MenubarSubTrigger,
|
||||
MenubarTrigger,
|
||||
} from "@suaveplan/react-ui";
|
||||
import { useState } from "react";
|
||||
|
||||
export function FullMenubar() {
|
||||
const [showToolbar, setShowToolbar] = useState(true);
|
||||
const [position, setPosition] = useState<"top" | "bottom">("bottom");
|
||||
|
||||
return (
|
||||
<Menubar>
|
||||
<MenubarMenu>
|
||||
<MenubarTrigger>File</MenubarTrigger>
|
||||
<MenubarContent>
|
||||
<MenubarLabel>Editor</MenubarLabel>
|
||||
<MenubarSeparator />
|
||||
<MenubarGroup>
|
||||
<MenubarItem>
|
||||
New file
|
||||
<MenubarShortcut>⌘N</MenubarShortcut>
|
||||
</MenubarItem>
|
||||
<MenubarItem>
|
||||
Open file
|
||||
<MenubarShortcut>⌘O</MenubarShortcut>
|
||||
</MenubarItem>
|
||||
</MenubarGroup>
|
||||
<MenubarSeparator />
|
||||
<MenubarSub>
|
||||
<MenubarSubTrigger>Recent</MenubarSubTrigger>
|
||||
<MenubarSubContent>
|
||||
<MenubarItem>doc1.md</MenubarItem>
|
||||
<MenubarItem>doc2.md</MenubarItem>
|
||||
</MenubarSubContent>
|
||||
</MenubarSub>
|
||||
</MenubarContent>
|
||||
</MenubarMenu>
|
||||
<MenubarMenu>
|
||||
<MenubarTrigger>View</MenubarTrigger>
|
||||
<MenubarContent>
|
||||
<MenubarCheckboxItem
|
||||
checked={showToolbar}
|
||||
onCheckedChange={(v) => setShowToolbar(Boolean(v))}
|
||||
>
|
||||
Toolbar
|
||||
</MenubarCheckboxItem>
|
||||
<MenubarRadioGroup
|
||||
value={position}
|
||||
onValueChange={(v) =>
|
||||
setPosition(v as "top" | "bottom")
|
||||
}
|
||||
>
|
||||
<MenubarRadioItem value="top">
|
||||
Toolbar at top
|
||||
</MenubarRadioItem>
|
||||
<MenubarRadioItem value="bottom">
|
||||
Toolbar at bottom
|
||||
</MenubarRadioItem>
|
||||
</MenubarRadioGroup>
|
||||
</MenubarContent>
|
||||
</MenubarMenu>
|
||||
</Menubar>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Menubar>`
|
||||
|
||||
The Radix Root. Standard Radix Root props (`value`, `defaultValue`, `onValueChange`, `dir`, `loop`).
|
||||
|
||||
### `<MenubarMenu>`
|
||||
|
||||
Wraps a single top-level menu (a trigger + its content). Required by Radix.
|
||||
|
||||
### `<MenubarTrigger>`
|
||||
|
||||
The visible top-bar label that opens its menu. Renders a `<button>`.
|
||||
|
||||
### `<MenubarContent>`
|
||||
|
||||
Auto-portalled floating menu surface. Standard Radix Content props.
|
||||
|
||||
### `<MenubarItem>`
|
||||
|
||||
A single menu item.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `inset` | `boolean` | `false` | Adds 32-px left gutter. |
|
||||
| `disabled` | `boolean` | `false` | Skips in keyboard nav and ignores activation. |
|
||||
| `onSelect` | `(event: Event) => void` | _none_ | Fires on activation. |
|
||||
|
||||
### `<MenubarGroup>`, `<MenubarLabel>`, `<MenubarSeparator>`
|
||||
|
||||
Standard menu structure. `<MenubarLabel>` accepts an `inset` prop.
|
||||
|
||||
### `<MenubarShortcut>`
|
||||
|
||||
Keyboard-shortcut hint pinned at the end of an item. Children supplied by the consumer (the chassis NEVER hardcodes shortcut text).
|
||||
|
||||
### `<MenubarCheckboxItem>`, `<MenubarRadioGroup>`, `<MenubarRadioItem>`
|
||||
|
||||
Standard Radix props.
|
||||
|
||||
### `<MenubarSub>`, `<MenubarSubTrigger>`, `<MenubarSubContent>`
|
||||
|
||||
Nested sub-menu. `<MenubarSubTrigger>` accepts `inset` and renders a chevron (`rtl:rotate-180`).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis NEVER assigns a numeric `z-index` — Radix portals handle stacking.
|
||||
- The check / dot indicators sit at `start-2` (logical property) so they flip in RTL.
|
||||
- `motion-reduce:animate-none` collapses the open/close animation.
|
||||
- The trigger active state lifts to `data-[state=open]:bg-accent` so the open menu's trigger reads visually distinct from idle siblings.
|
||||
- The `<MenubarShortcut>` deliberately accepts only `children` (no `ariaKeyShortcutsKey` like `<DropdownMenuShortcut>`) — menubar shortcuts conventionally are presentational accelerator hints next to the visible label, and the trigger's own underline-hint convention covers the screen-reader announcement.
|
||||
- Per the shadcn skill rule, every top-level menu MUST be wrapped in `<MenubarMenu>`.
|
||||
@@ -0,0 +1,89 @@
|
||||
# Menubar Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`menubarVariants`, `menubarTriggerVariants`, `menubarContentVariants`, `menubarItemVariants`, `menubarLabelVariants`, `menubarSeparatorVariants`, `menubarShortcutVariants`, `menubarCheckboxItemVariants`, `menubarRadioItemVariants`, and `menubarSubTriggerVariants` are the cva (class-variance-authority) configurations powering every `<Menubar>` slot. They are split from `menubar.tsx` into their own module so an ejected source copy under `npx shadcn@latest add @suaveplan/menubar` keeps the cva configurations in a dedicated file. Theme overrides flow through `useThemedVariants("Menubar<Slot>")`.
|
||||
|
||||
The root cva renders the menubar as a horizontal pill (`flex h-10 items-center space-x-1 rounded-md border bg-background p-1`); triggers and content reuse the dropdown-menu pattern. The Item / Label / SubTrigger cvas all carry an `inset` axis for alignment with checkbox / radio rows.
|
||||
|
||||
## Features
|
||||
|
||||
- Root: horizontal pill with `border bg-background` so the menubar reads as a distinct surface.
|
||||
- Trigger: focus-bg lift + open-state lift (both via Radix `data-state="open"`).
|
||||
- Content: `min-w-[12rem]` floating surface with side-aware slide-in animations.
|
||||
- Item / Label / SubTrigger: focus-bg, optional `ps-8` inset gutter.
|
||||
- Separator / Shortcut: standard divider + muted-foreground row terminator.
|
||||
- Checkbox / Radio: `ps-8 pe-2` to leave indicator space.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { menubarItemVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const className = menubarItemVariants({ inset: true });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { menubarVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CompactMenubar({
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className={cn(menubarVariants(), "h-8 text-xs", className)}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `menubarVariants(): string`
|
||||
|
||||
Root pill class string. No arguments.
|
||||
|
||||
### `menubarTriggerVariants(): string`
|
||||
|
||||
Top-bar trigger class string. No arguments.
|
||||
|
||||
### `menubarContentVariants(): string`
|
||||
|
||||
Floating menu surface class string. No arguments.
|
||||
|
||||
### `menubarItemVariants(props?: { inset?: boolean }): string`
|
||||
|
||||
Item class string. Defaults to `{ inset: false }`.
|
||||
|
||||
### `menubarLabelVariants(props?: { inset?: boolean }): string`
|
||||
|
||||
Label class string. Defaults to `{ inset: false }`.
|
||||
|
||||
### `menubarSeparatorVariants(): string`
|
||||
|
||||
Separator class string.
|
||||
|
||||
### `menubarShortcutVariants(): string`
|
||||
|
||||
Shortcut text class string.
|
||||
|
||||
### `menubarCheckboxItemVariants(): string`, `menubarRadioItemVariants(): string`
|
||||
|
||||
Checkbox / radio row class strings.
|
||||
|
||||
### `menubarSubTriggerVariants(props?: { inset?: boolean }): string`
|
||||
|
||||
Sub-trigger class string. Defaults to `{ inset: false }`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Per the shadcn skill rule, no numeric `z-index` is assigned anywhere — Radix portals handle stacking.
|
||||
- The check / dot indicators sit at `start-2` (logical property) so they flip in RTL.
|
||||
- `motion-reduce:animate-none` collapses the open/close animation.
|
||||
- The root border + background let the menubar live above any page surface (light / dark / image backdrop) without colour clashes.
|
||||
205
docs/ui/react/react-ui/src/components/pagination/pagination.md
Normal file
205
docs/ui/react/react-ui/src/components/pagination/pagination.md
Normal file
@@ -0,0 +1,205 @@
|
||||
# Pagination
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - pagination.variants — `paginationVariants`, `pagina…
|
||||
- [pagination.variants](./pagination.variants.md) — `paginationVariants`, `paginationContentVariants`, `paginationItemVariants`, `paginationLinkVariants`, `paginationPrevN…
|
||||
- pagination.variants — `paginationVariants`, `pagina…
|
||||
|
||||
- [pagination.variants](./pagination.variants.md) — `paginationVariants`, `paginationContentVariants`, `paginationItemVariants`, `paginationLinkVariants`, `paginationPrevN…
|
||||
|
||||
- pagination.variants — `paginationVariants`, `pagina…
|
||||
|
||||
- [pagination.variants](./pagination.variants.md) — `paginationVariants`, `paginationContentVariants`, `paginationItemVariants`, `paginationLinkVariants`, `paginationPrevN…
|
||||
|
||||
- pagination.variants — `paginationVariants`, `pagina…
|
||||
|
||||
- [pagination.variants](./pagination.variants.md) — `paginationVariants`, `paginationContentVariants`, `paginationItemVariants`, `paginationLinkVariants`, `paginationPrevN…
|
||||
- [pagination.variants](./pagination.variants.md) — `paginationVariants`, `paginationContentVariants`, `paginationItemVariants`, `paginationLinkVariants`, `paginationPrevN…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Pagination>` and its sub-components (`<PaginationContent>`, `<PaginationItem>`, `<PaginationLink>`, `<PaginationPrevious>`, `<PaginationNext>`, `<PaginationEllipsis>`, `<PaginationStatus>`) are the chassis page-navigation primitive. Pure HTML composition — no Radix wrapper — so the markup is deterministic and trivially server-rendered. The wrapper is a semantic `<nav>` whose accessible name flows through the bundled `react-ui.pagination.label` translation key. Inside, an unordered list (`<PaginationContent>`) carries page link rows (`<PaginationLink>`) interleaved with previous / next / ellipsis affordances. The current page sets `isActive`, which adds `aria-current="page"` so screen-readers announce it as the active page.
|
||||
|
||||
The "Page X of Y" status caption (`<PaginationStatus>`) renders through a real ICU MessageFormat via `@suaveplan/i18n` `MessageParser` so the locale's plural rule chooses the right form. Welsh covers six plural categories (`zero | one | two | few | many | other`), and the chassis test suite exercises each one against the bundled `cy-GB` template post-wireup.
|
||||
|
||||
## Features
|
||||
|
||||
- Semantic `<nav>` + `<ul>` + `<li>` markup — pure HTML; SSR-trivial; no JavaScript required for the trail to render.
|
||||
- Translated `aria-label` on the wrapper (`react-ui.pagination.label`).
|
||||
- `<PaginationLink isActive>` carries `aria-current="page"` so screen-readers announce the active page.
|
||||
- `<PaginationPrevious>` / `<PaginationNext>` carry translated `aria-label`s (`react-ui.pagination.previous` / `react-ui.pagination.next`) and RTL-flipped chevron icons.
|
||||
- `<PaginationEllipsis>` provides a collapsed-page indicator with `aria-hidden="true"` plus a translated `aria-label` for assistive tech.
|
||||
- `<PaginationStatus>` renders "Page X of Y" via `MessageParser` ICU plural so locale-appropriate plurals fire — Welsh's six-form plural is exercised end-to-end.
|
||||
- Three size variants on `<PaginationLink>` (`default` / `sm` / `icon`).
|
||||
- All sub-components forward refs to their underlying HTML elements.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Pagination,
|
||||
PaginationContent,
|
||||
PaginationEllipsis,
|
||||
PaginationItem,
|
||||
PaginationLink,
|
||||
PaginationNext,
|
||||
PaginationPrevious,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function PagedList({ page }: { page: number }) {
|
||||
return (
|
||||
<Pagination>
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<PaginationPrevious href={`?page=${page - 1}`} />
|
||||
</PaginationItem>
|
||||
{[1, 2, 3].map((n) => (
|
||||
<PaginationItem key={n}>
|
||||
<PaginationLink
|
||||
href={`?page=${n}`}
|
||||
isActive={n === page}
|
||||
>
|
||||
{n}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
))}
|
||||
<PaginationItem>
|
||||
<PaginationEllipsis />
|
||||
</PaginationItem>
|
||||
<PaginationItem>
|
||||
<PaginationNext href={`?page=${page + 1}`} />
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Pagination,
|
||||
PaginationContent,
|
||||
PaginationEllipsis,
|
||||
PaginationItem,
|
||||
PaginationLink,
|
||||
PaginationNext,
|
||||
PaginationPrevious,
|
||||
PaginationStatus,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
interface Props {
|
||||
readonly current: number;
|
||||
readonly total: number;
|
||||
readonly buildHref: (page: number) => string;
|
||||
}
|
||||
|
||||
function paginationWindow(
|
||||
current: number,
|
||||
total: number
|
||||
): ReadonlyArray<number | null> {
|
||||
if (total <= 7) {
|
||||
return Array.from({ length: total }, (_, i) => i + 1);
|
||||
}
|
||||
if (current <= 3) return [1, 2, 3, 4, null, total - 1, total];
|
||||
if (current >= total - 2) {
|
||||
return [1, 2, null, total - 3, total - 2, total - 1, total];
|
||||
}
|
||||
return [1, null, current - 1, current, current + 1, null, total];
|
||||
}
|
||||
|
||||
export function FullPagination({ current, total, buildHref }: Props) {
|
||||
const items = paginationWindow(current, total);
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<Pagination>
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<PaginationPrevious
|
||||
href={buildHref(Math.max(1, current - 1))}
|
||||
/>
|
||||
</PaginationItem>
|
||||
{items.map((entry, i) => (
|
||||
<PaginationItem key={`${entry}-${i}`}>
|
||||
{entry === null ? (
|
||||
<PaginationEllipsis />
|
||||
) : (
|
||||
<PaginationLink
|
||||
href={buildHref(entry)}
|
||||
isActive={entry === current}
|
||||
>
|
||||
{entry}
|
||||
</PaginationLink>
|
||||
)}
|
||||
</PaginationItem>
|
||||
))}
|
||||
<PaginationItem>
|
||||
<PaginationNext
|
||||
href={buildHref(Math.min(total, current + 1))}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
<PaginationStatus current={current} total={total} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Pagination>`
|
||||
|
||||
Renders a `<nav role="navigation">` with a translated `aria-label`.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `ariaLabelKey` | `TranslationKey` | `react-ui.pagination.label` | Override the accessible name. |
|
||||
|
||||
### `<PaginationContent>`, `<PaginationItem>`
|
||||
|
||||
Pure layout — `<ul>` and `<li>` containers.
|
||||
|
||||
### `<PaginationLink>`
|
||||
|
||||
Renders an `<a>`. Sets `aria-current="page"` when `isActive` is true.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `size` | `"default" \| "sm" \| "icon"` | `"icon"` | Visual size. |
|
||||
| `isActive` | `boolean` | `false` | Marks as the current page. |
|
||||
|
||||
### `<PaginationPrevious>`, `<PaginationNext>`
|
||||
|
||||
Arrow affordances with translated `aria-label`s and chevron icons.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `ariaLabelKey` | `TranslationKey` | `react-ui.pagination.previous` / `.next` | Override the accessible name. |
|
||||
|
||||
### `<PaginationEllipsis>`
|
||||
|
||||
Collapsed-page indicator. Pure visual filler with `aria-hidden="true"` and a translated `aria-label` (`react-ui.pagination.morePages`).
|
||||
|
||||
### `<PaginationStatus>`
|
||||
|
||||
The "Page X of Y" caption.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `current` | `number` | _required_ | Active page number. |
|
||||
| `total` | `number` | _required_ | Total page count. |
|
||||
| `statusKey` | `TranslationKey` | `react-ui.pagination.pageXofY` | Override the ICU template key. |
|
||||
|
||||
The template format is `"Page {current} of {total}"` in `en-GB`, with locale-appropriate plural variants in the other bundled locales.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis NEVER wraps pagination in a Radix primitive — pure HTML keeps SSR trivial and matches shadcn upstream's recommendation.
|
||||
- `<PaginationPrevious>` / `<PaginationNext>` chevron icons carry `rtl:rotate-180` so the arrows flip in RTL contexts.
|
||||
- The `<PaginationStatus>` uses `aria-live="polite"` and `aria-atomic="true"` so screen-readers announce updates as the user navigates without interrupting the focus flow.
|
||||
- `MessageParser` is constructed once per render via `useMemo` so the parser cache is preserved across re-renders.
|
||||
- Per the Wave 3d reconciliation finding (#4), the `t()` call returns the ICU template, then `messageParser.format(template, values, { locale })` performs interpolation and plural resolution.
|
||||
- Per the Wave 3d reconciliation finding (#2), the active locale is read via the `(i18n as { readonly locale: string }).locale` cast since `useTranslation()` throws `REACT_I18N.NO_PROVIDER` in dev mode if called outside a provider, making the optional-chain branch unreachable.
|
||||
@@ -0,0 +1,77 @@
|
||||
# Pagination Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`paginationVariants`, `paginationContentVariants`, `paginationItemVariants`, `paginationLinkVariants`, `paginationPrevNextVariants`, `paginationEllipsisVariants`, and `paginationStatusVariants` are the cva (class-variance-authority) configurations powering every `<Pagination>` slot. They are split from `pagination.tsx` into their own module so an ejected source copy under `npx shadcn@latest add @suaveplan/pagination` keeps the cva configurations in a dedicated file. Theme overrides flow through `useThemedVariants("Pagination<Slot>")`.
|
||||
|
||||
The pagination wrapper centres itself horizontally with `mx-auto justify-center` so it sits centred under a paged surface by default; consumers needing a left-aligned trail compose with `cn(...)` to override.
|
||||
|
||||
## Features
|
||||
|
||||
- Wrapper: `mx-auto flex w-full justify-center` so the trail centres in the available width.
|
||||
- Content: flex-row with `gap-1` between page items.
|
||||
- Link: shadcn-aligned button conventions (`h-9 px-4` default; `h-8 px-3` sm; `h-9 w-9` icon); active variant adds a border and background; focus-visible ring; smooth-easing colour transition with `motion-reduce:transition-none`.
|
||||
- PrevNext: tuned spacing (`gap-1 px-2.5`) for the chevron + label combo.
|
||||
- Ellipsis: `flex h-9 w-9` square hit-area matching icon-sized links.
|
||||
- Status: `text-sm text-muted-foreground` so the caption recedes under the page heading.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
paginationLinkVariants,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
const className = paginationLinkVariants({ size: "icon", isActive: true });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { paginationContentVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CompactContent({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<ul className={cn(paginationContentVariants(), "gap-0.5 text-xs")}>
|
||||
{children}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `paginationVariants(): string`
|
||||
|
||||
Wrapper class string. No arguments.
|
||||
|
||||
### `paginationContentVariants(): string`
|
||||
|
||||
`<ul>` class string. No arguments.
|
||||
|
||||
### `paginationItemVariants(): string`
|
||||
|
||||
`<li>` class string. No arguments (currently empty by design).
|
||||
|
||||
### `paginationLinkVariants(props?: { size?: "default" | "sm" | "icon"; isActive?: boolean }): string`
|
||||
|
||||
Page-button class string. Defaults to `{ size: "icon", isActive: false }`.
|
||||
|
||||
### `paginationPrevNextVariants(): string`
|
||||
|
||||
Tuned class string for previous/next combo (gap + horizontal padding).
|
||||
|
||||
### `paginationEllipsisVariants(): string`
|
||||
|
||||
Ellipsis hit-area class string.
|
||||
|
||||
### `paginationStatusVariants(): string`
|
||||
|
||||
"Page X of Y" caption class string.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The link cva uses `focus-visible` (not `focus`) so the ring only appears for keyboard users.
|
||||
- `motion-reduce:transition-none` collapses the colour transition for users with `prefers-reduced-motion: reduce`.
|
||||
- The `isActive` axis adds a border + background so the active page reads as a button-shaped indicator, matching shadcn upstream's pagination treatment.
|
||||
- Per the shadcn skill rule, no numeric `z-index` is assigned anywhere in the cva — pagination is in the document flow, not a stacking layer.
|
||||
130
docs/ui/react/react-ui/src/components/popover/popover.md
Normal file
130
docs/ui/react/react-ui/src/components/popover/popover.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# Popover
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - popover.variants — `popoverContentVariants` is the……
|
||||
- [popover.variants](./popover.variants.md) — `popoverContentVariants` is the cva configuration powering the `<PopoverContent>` floating panel. It is split from `pop…
|
||||
- popover.variants — `popoverContentVariants` is the……
|
||||
|
||||
- [popover.variants](./popover.variants.md) — `popoverContentVariants` is the cva configuration powering the `<PopoverContent>` floating panel. It is split from `pop…
|
||||
|
||||
- popover.variants — `popoverContentVariants` is the……
|
||||
|
||||
- [popover.variants](./popover.variants.md) — `popoverContentVariants` is the cva configuration powering the `<PopoverContent>` floating panel. It is split from `pop…
|
||||
|
||||
- popover.variants — `popoverContentVariants` is the…
|
||||
|
||||
- [popover.variants](./popover.variants.md) — `popoverContentVariants` is the cva configuration powering the `<PopoverContent>` floating panel. It is split from `pop…
|
||||
- [popover.variants](./popover.variants.md) — `popoverContentVariants` is the cva configuration powering the `<PopoverContent>` floating panel. It is split from `pop…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Popover>` and its sub-components (`<PopoverTrigger>`, `<PopoverAnchor>`, `<PopoverPortal>`, `<PopoverContent>`, `<PopoverArrow>`, `<PopoverClose>`) are the chassis floating-panel primitives built on `@radix-ui/react-popover`. Use this for non-modal ephemeral content — info bubbles, compact pickers, link previews — where focus does NOT need to be trapped.
|
||||
|
||||
Distinct from `<Dialog>` and `<AlertDialog>`: a popover is non-modal so focus is not trapped, clicking outside dismisses it, and Escape dismisses it. There is no required title (popovers narrate their accessible name via their content). Radix handles the floating-UI math (avoid-collisions, sticky positioning, side/align resolution); the chassis adds the cva visual recipe.
|
||||
|
||||
## Features
|
||||
|
||||
- Side / align positional API (`side`: top/right/bottom/left; `align`: start/center/end).
|
||||
- `sideOffset` / `alignOffset` numeric offsets pass through to Radix.
|
||||
- Self-portals via `<PopoverPortal>` so consumers do not need to compose the portal manually.
|
||||
- Click-outside dismissal.
|
||||
- Escape closes.
|
||||
- Optional `<PopoverArrow>` decoration pointing back at the trigger.
|
||||
- Optional `<PopoverAnchor>` lets the visual trigger differ from the popover anchor (e.g. anchor to a parent container).
|
||||
- Theme-driven cva via `useThemedVariants("PopoverContent")`.
|
||||
- All sub-components forward refs.
|
||||
- `motion-reduce:animate-none` collapses the open/close animations under `prefers-reduced-motion: reduce`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function HelpBubble() {
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger>Help</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
Detailed help text for the focused control.
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Popover,
|
||||
PopoverArrow,
|
||||
PopoverAnchor,
|
||||
PopoverClose,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@suaveplan/react-ui";
|
||||
import { useState } from "react";
|
||||
|
||||
export function ColorPicker() {
|
||||
const [color, setColor] = useState("#3b82f6");
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
aria-label="Pick a colour"
|
||||
style={{ background: color, width: 24, height: 24 }}
|
||||
/>
|
||||
<PopoverContent side="bottom" align="start" sideOffset={8}>
|
||||
<PopoverArrow />
|
||||
<input
|
||||
type="color"
|
||||
value={color}
|
||||
onChange={(e) => setColor(e.target.value)}
|
||||
/>
|
||||
<PopoverClose>Done</PopoverClose>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Popover>`
|
||||
|
||||
Wraps the Radix Popover Root. Same `open` / `defaultOpen` / `onOpenChange` lifecycle props as the other overlays.
|
||||
|
||||
### `<PopoverTrigger>`
|
||||
|
||||
The visual trigger — a `<button>` by default; `asChild` polymorphic.
|
||||
|
||||
### `<PopoverContent>`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `side` | `"top" \| "right" \| "bottom" \| "left"` | `"bottom"` | Preferred side of the trigger. |
|
||||
| `align` | `"start" \| "center" \| "end"` | `"center"` | Alignment along the perpendicular axis. |
|
||||
| `sideOffset` | `number` | `4` | Numeric offset along the side axis. |
|
||||
| `alignOffset` | `number` | `0` | Numeric offset along the align axis. |
|
||||
| `avoidCollisions` | `boolean` | `true` | Allow Radix to reposition to avoid viewport edges. |
|
||||
| `collisionPadding` | `number \| Padding` | `0` | Padding from viewport edges. |
|
||||
|
||||
### `<PopoverAnchor>`
|
||||
|
||||
Optional — overrides the positioning anchor. When omitted, the popover anchors to the `<PopoverTrigger>`.
|
||||
|
||||
### `<PopoverArrow>`, `<PopoverClose>`, `<PopoverPortal>`
|
||||
|
||||
Direct re-exports of the Radix sub-components.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis NEVER assigns a numeric `z-index` — Radix portals handle stacking.
|
||||
- `<PopoverContent>` self-portals via `<PopoverPortal>` so consumers do not need to compose the portal manually.
|
||||
- `align="center"` and `sideOffset={4}` are chassis defaults (matching shadcn upstream) — overridable per call.
|
||||
- `data-side="<side>"` is used by the cva animation classes — Radix sets it once positioning is resolved (which may differ from the requested `side` if `avoidCollisions` kicked in).
|
||||
- For modal confirmation surfaces use `<Dialog>` or `<AlertDialog>` — Popover is intentionally non-modal so focus is NOT trapped.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Popover Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`popoverContentVariants` is the cva configuration powering the `<PopoverContent>` floating panel. It is split from `popover.tsx` into its own module so an ejected source copy under `npx shadcn@latest add @suaveplan/popover` keeps the cva configuration in a dedicated file. Theme overrides flow through `useThemedVariants("PopoverContent")`.
|
||||
|
||||
The cva carries the visual recipe (rounded border, popover-foreground colours, shadow, fixed width) plus open/close animations keyed off Radix's `data-state` and `data-side` attributes so the slide-in direction matches the resolved positioning.
|
||||
|
||||
## Features
|
||||
|
||||
- Fixed `w-72` width matching the shadcn upstream popover recipe.
|
||||
- `data-state` open/close fade + zoom animations.
|
||||
- `data-side` slide-in direction: bottom-anchored panels slide from the top, etc.
|
||||
- `bg-popover text-popover-foreground` so the panel reads against any surface.
|
||||
- `motion-reduce:animate-none` collapses animations under reduced-motion preference.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { popoverContentVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const className = popoverContentVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { popoverContentVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomPopoverShell() {
|
||||
return (
|
||||
<div
|
||||
data-state="open"
|
||||
data-side="bottom"
|
||||
className={cn(popoverContentVariants(), "w-96 border-primary")}
|
||||
>
|
||||
…
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `popoverContentVariants(): string`
|
||||
|
||||
Returns the popover content classes; takes no arguments — the popover is not parameterised by an axis.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Per the shadcn skill rule, the cva NEVER assigns a numeric `z-index`. Radix's `<Portal>` mounts the content above all page content via DOM placement.
|
||||
- The slide-in classes (`slide-in-from-top-2`, `slide-in-from-right-2`, …) are keyed off Radix's `data-side` so the animation direction follows the resolved positioning rather than the requested side (Radix may flip if `avoidCollisions` triggered).
|
||||
94
docs/ui/react/react-ui/src/components/progress/progress.md
Normal file
94
docs/ui/react/react-ui/src/components/progress/progress.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# Progress
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - progress.variants — `progressVariants` is the cva c…
|
||||
- [progress.variants](./progress.variants.md) — `progressVariants` is the cva config that powers the `<Progress>` chassis component (paired with `progressIndicatorVari…
|
||||
- progress.variants — `progressVariants` is the cva c…
|
||||
|
||||
- [progress.variants](./progress.variants.md) — `progressVariants` is the cva config that powers the `<Progress>` chassis component (paired with `progressIndicatorVari…
|
||||
|
||||
- progress.variants — `progressVariants` is the cva c…
|
||||
|
||||
- [progress.variants](./progress.variants.md) — `progressVariants` is the cva config that powers the `<Progress>` chassis component (paired with `progressIndicatorVari…
|
||||
|
||||
- progress.variants — `progressVariants` is the cva c…
|
||||
|
||||
- [progress.variants](./progress.variants.md) — `progressVariants` is the cva config that powers the `<Progress>` chassis component (paired with `progressIndicatorVari…
|
||||
- [progress.variants](./progress.variants.md) — `progressVariants` is the cva config that powers the `<Progress>` chassis component (paired with `progressIndicatorVari…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Progress>` is the chassis progress bar built on `@radix-ui/react-progress`. It supports both indeterminate (Radix's loading-state animation when `value === undefined`) and determinate (`value` is `0..max`, default `max=100`) modes. The chassis layers a `size` axis on top — `sm` (4px), `default` (8px), `lg` (12px) — while leaving width consumer-driven via `className` (typically `w-full` inside a card or form row).
|
||||
|
||||
The accessible name comes from the consumer-supplied `aria-label` (typed as `TranslationKey`); when absent, the chassis falls back to the bundled `react-ui.progress.fallback` translation key so screen-readers always announce something meaningful. This avoids the common bug where developers ship a progress bar with no accessible name and screen-readers read out the percentage without any context.
|
||||
|
||||
## Features
|
||||
|
||||
- Indeterminate mode when `value === undefined` (Radix runs its loading animation).
|
||||
- Determinate mode when `value` is a number `0..max`.
|
||||
- Three sizes via the `size` axis (`sm` / `default` / `lg`).
|
||||
- `aria-label` typed as `TranslationKey` — falls back to bundled `react-ui.progress.fallback` when undefined.
|
||||
- `motion-safe:transition-transform motion-reduce:transition-none` on the indicator so reduced-motion users see instant updates.
|
||||
- Forwards refs to the Radix Root.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { Progress } from "@suaveplan/react-ui";
|
||||
|
||||
export function UploadProgress({ percent }: { percent: number }) {
|
||||
return <Progress value={percent} />;
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { Progress } from "@suaveplan/react-ui";
|
||||
|
||||
export function MultiStageUpload({
|
||||
currentStage,
|
||||
totalStages,
|
||||
isLoading,
|
||||
}: {
|
||||
currentStage: number;
|
||||
totalStages: number;
|
||||
isLoading: boolean;
|
||||
}) {
|
||||
return (
|
||||
<Progress
|
||||
size="lg"
|
||||
value={isLoading ? undefined : (currentStage / totalStages) * 100}
|
||||
aria-label="react-ui.loading"
|
||||
className="w-64"
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Progress>`
|
||||
|
||||
`forwardRef<…, ProgressProps>` — Radix `Root` wrapper that renders an inner `Indicator` translated based on `value`.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `value` | `number \| undefined` | `undefined` | When defined, determinate mode; when undefined, indeterminate mode. |
|
||||
| `max` | `number` | `100` | Forwarded to Radix as the upper bound for `value`. |
|
||||
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Bar height. |
|
||||
| `aria-label` | `TranslationKey \| undefined` | `react-ui.progress.fallback` | Accessible name. Falls back to bundled key. |
|
||||
| _Radix props_ | _Radix-typed_ | — | All `ProgressPrimitive.Root` props (except `aria-label` which the chassis overrides). |
|
||||
|
||||
#### Errors
|
||||
|
||||
The Progress component calls `useTranslation()` to resolve the `aria-label` translation key. It MUST render inside a `<TranslationProvider>` / `<ReactUiProvider>`; otherwise `useTranslation` throws.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- When `value === undefined` the chassis passes `null` to Radix (matching Radix's API for indeterminate mode). Radix toggles its `data-state="indeterminate"` on the root and runs its built-in loading animation; consumers wanting a custom indeterminate animation override the indicator's `transform` via a `[&>*]:` arbitrary descendant.
|
||||
- The accessible-name fallback uses the bundled `react-ui.progress.fallback` key which all five locales translate. This guarantees the screen-reader announcement is meaningful even when consumers forget to supply their own label.
|
||||
- `motion-safe:transition-transform` only runs when the user has not requested reduced motion. Reduced-motion users see the indicator snap to its new position instantly — this is intentional, animated progress with reduced-motion can cause vestibular discomfort.
|
||||
- The chassis does NOT expose a `getValueLabel` prop (Radix supports it) because the chassis-supplied `aria-label` is sufficient for the common case; consumers wanting a percentage-readable label compose their own `<span>{percent}%</span>` next to the bar.
|
||||
- The chassis supports both indeterminate (loading) and determinate (`0..100`) modes from a single component because Radix already handles the state-machine internally; splitting them into two components would force consumers to refactor when the loading-known transition lands.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Progress Variants
|
||||
|
||||
## Purpose
|
||||
|
||||
`progressVariants` is the cva config that powers the `<Progress>` chassis component (paired with `progressIndicatorVariants` for the inner translate target). The root exposes a `size` axis with three rungs (`sm` 4px, `default` 8px, `lg` 12px) that swap the bar height; width is consumer-driven via `className` (typically `w-full` inside a card or form row).
|
||||
|
||||
## Features
|
||||
|
||||
- Single `size` axis with three rungs (`sm` / `default` / `lg`) controlling bar height.
|
||||
- `rounded-full overflow-hidden` so the indicator clips to the rounded ends.
|
||||
- `bg-secondary` track + `bg-primary` indicator — both colours come from the active theme tokens.
|
||||
- `motion-safe:transition-transform motion-reduce:transition-none` on the indicator so reduced-motion users see instant updates.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { progressVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const sm = progressVariants({ size: "sm" });
|
||||
const default_ = progressVariants();
|
||||
const lg = progressVariants({ size: "lg" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { cva } from "class-variance-authority";
|
||||
import { progressVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const successProgress = cva(
|
||||
[progressVariants(), "[&>*]:bg-emerald-500"].join(" ")
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `progressVariants(opts?)`
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Bar height. |
|
||||
|
||||
### `progressIndicatorVariants()`
|
||||
|
||||
No options — single canonical class string. Indicator fills the parent (`size-full flex-1`) and is translated horizontally based on Radix's `value` prop (handled by the chassis component).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The bar height uses fixed Tailwind classes (`h-1` / `h-2` / `h-3`) rather than `size-*` because the bar's width is consumer-driven and the height should remain constant across viewport sizes.
|
||||
- `bg-secondary` is the track colour and `bg-primary` is the indicator colour — both come from the active theme tokens. Consumers wanting custom colours can either register a `variantOverrides.Progress` or override at the call site via `className`.
|
||||
- The indicator uses `motion-safe:transition-transform` so the animated translation only runs when the user has not requested reduced motion. Reduced-motion users see the indicator snap to its target position instantly.
|
||||
- `overflow-hidden` on the root clips the indicator's left edge so it appears to fill from the start rather than being a separate floating bar.
|
||||
147
docs/ui/react/react-ui/src/components/radio-group/radio-group.md
Normal file
147
docs/ui/react/react-ui/src/components/radio-group/radio-group.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# RadioGroup
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - radio-group.variants — `radioGroupVariants` and `ra…
|
||||
- [radio-group.variants](./radio-group.variants.md) — `radioGroupVariants` and `radioGroupItemVariants` are the cva (class-variance-authority) configurations powering `<Radi…
|
||||
- radio-group.variants — `radioGroupVariants` and `ra…
|
||||
|
||||
- [radio-group.variants](./radio-group.variants.md) — `radioGroupVariants` and `radioGroupItemVariants` are the cva (class-variance-authority) configurations powering `<Radi…
|
||||
|
||||
- radio-group.variants — `radioGroupVariants` and `ra…
|
||||
|
||||
- [radio-group.variants](./radio-group.variants.md) — `radioGroupVariants` and `radioGroupItemVariants` are the cva (class-variance-authority) configurations powering `<Radi…
|
||||
|
||||
- radio-group.variants — `radioGroupVariants` and `ra…
|
||||
|
||||
- [radio-group.variants](./radio-group.variants.md) — `radioGroupVariants` and `radioGroupItemVariants` are the cva (class-variance-authority) configurations powering `<Radi…
|
||||
- [radio-group.variants](./radio-group.variants.md) — `radioGroupVariants` and `radioGroupItemVariants` are the cva (class-variance-authority) configurations powering `<Radi…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<RadioGroup>` and `<RadioGroupItem>` are the chassis radio-group primitives built on `@radix-ui/react-radio-group`. Radix supplies the a11y semantics (`role="radiogroup"` on the container, `role="radio"` on each item, roving tabindex across items, arrow-key navigation, native `Space` activation, controlled `value` propagation); the chassis layers a vertical-stack flex/gap layout via cva and a circular indicator dot rendered with `<CircleIcon fill-current>` from `lucide-react`.
|
||||
|
||||
Per the shadcn convention, items always sit inside the group via `<RadioGroupItem>` — rendering an item without a group is unsupported. Both components forward refs to their underlying Radix elements so consumers can focus / measure / drive animations.
|
||||
|
||||
## Features
|
||||
|
||||
- Container renders a `<div role="radiogroup">` with `grid gap-2` so items stack vertically with a small uniform gap (NOT `space-y-*` per the shadcn skill rule).
|
||||
- Items render a `<button role="radio">` with the circular `border-primary` outline.
|
||||
- Three sizes for items (`sm`, `default`, `lg`) scaling the radio circle.
|
||||
- Indicator dot rendered via `<CircleIcon fill-current text-current>` so the dot inherits the surrounding text colour and the chassis switches it without an extra theme prop.
|
||||
- Controlled and uncontrolled use via Radix `value` / `defaultValue` / `onValueChange`.
|
||||
- `disabled` per-item or per-group blocks pointer + keyboard input.
|
||||
- Refs forwarded on both `<RadioGroup>` and `<RadioGroupItem>`.
|
||||
- `motion-reduce:transition-none` collapses transitions for `prefers-reduced-motion: reduce`.
|
||||
- Theme-driven variant overrides via `useThemedVariants("RadioGroup" | "RadioGroupItem")`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { RadioGroup, RadioGroupItem } from "@suaveplan/react-ui";
|
||||
|
||||
export function PlanPicker() {
|
||||
return (
|
||||
<RadioGroup defaultValue="pro">
|
||||
<div className="flex items-center gap-2">
|
||||
<RadioGroupItem value="free" id="plan-free" />
|
||||
<label htmlFor="plan-free">Free</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<RadioGroupItem value="pro" id="plan-pro" />
|
||||
<label htmlFor="plan-pro">Pro</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<RadioGroupItem value="team" id="plan-team" />
|
||||
<label htmlFor="plan-team">Team</label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { RadioGroup, RadioGroupItem, FieldSet, FieldLegend } from "@suaveplan/react-ui";
|
||||
import { useState } from "react";
|
||||
|
||||
interface DeliverySpeed {
|
||||
readonly id: string;
|
||||
readonly label: string;
|
||||
readonly description: string;
|
||||
readonly disabled?: boolean;
|
||||
}
|
||||
|
||||
export function DeliveryPicker({ options }: { options: readonly DeliverySpeed[] }) {
|
||||
const [speed, setSpeed] = useState("standard");
|
||||
|
||||
return (
|
||||
<FieldSet>
|
||||
<FieldLegend>Delivery speed</FieldLegend>
|
||||
<RadioGroup value={speed} onValueChange={setSpeed}>
|
||||
{options.map((opt) => (
|
||||
<div key={opt.id} className="flex items-start gap-2">
|
||||
<RadioGroupItem
|
||||
value={opt.id}
|
||||
id={`delivery-${opt.id}`}
|
||||
disabled={opt.disabled}
|
||||
className="mt-1"
|
||||
/>
|
||||
<div>
|
||||
<label htmlFor={`delivery-${opt.id}`}>{opt.label}</label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{opt.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</FieldSet>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<RadioGroup>`
|
||||
|
||||
`forwardRef<ComponentRef<typeof RadioGroupPrimitive.Root>, RadioGroupProps>` — renders the Radix Root.
|
||||
|
||||
#### `RadioGroupProps`
|
||||
|
||||
Inherits every prop from Radix `RadioGroupPrimitive.Root` except `asChild`. Common props:
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `value` | `string` | _uncontrolled_ | Currently selected value. |
|
||||
| `defaultValue` | `string` | _none_ | Initial value for uncontrolled use. |
|
||||
| `onValueChange` | `(value: string) => void` | _none_ | Fires on value change. |
|
||||
| `disabled` | `boolean` | `false` | Disables every item. |
|
||||
| `name` | `string` | _none_ | Native form-field name. |
|
||||
| `orientation` | `"horizontal" \| "vertical"` | `"vertical"` | Arrow-key navigation axis. |
|
||||
|
||||
### `<RadioGroupItem>`
|
||||
|
||||
`forwardRef<ComponentRef<typeof RadioGroupPrimitive.Item>, RadioGroupItemProps>` — renders a Radix Item containing the indicator dot.
|
||||
|
||||
#### `RadioGroupItemProps`
|
||||
|
||||
Inherits every prop from Radix `RadioGroupPrimitive.Item` except `asChild`. Adds:
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Visual variant scaling the radio circle. |
|
||||
| `value` | `string` | _required_ | Unique value within the surrounding group. |
|
||||
|
||||
#### `radioGroupVariants` / `radioGroupItemVariants`
|
||||
|
||||
Both bare cva resolvers exported for external composition.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The container uses `grid gap-2` rather than `flex flex-col gap-2` because `grid` collapses cross-axis layout in a way that keeps the items column-aligned even when they have wildly different content lengths (label + description blocks).
|
||||
- Per the shadcn skill rule, the chassis NEVER uses `space-y-*` — `grid gap-*` flows the same in RTL.
|
||||
- The indicator dot uses `fill-current text-current` so it inherits the surrounding text colour automatically; theme overrides do not need to special-case the dot.
|
||||
- Refs target the Radix Root + Item. The Item ref attaches to the rendered `<button>` so consumers can `.focus()` programmatically.
|
||||
- Items that require a description/help text typically wrap the radio + content in `<div className="flex items-start gap-2">` with `mt-1` on the radio so the dot aligns with the first line of text.
|
||||
@@ -0,0 +1,61 @@
|
||||
# RadioGroup Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`radioGroupVariants` and `radioGroupItemVariants` are the cva (class-variance-authority) configurations powering `<RadioGroup>` and `<RadioGroupItem>`. They are split from `radio-group.tsx` into their own module so an ejected source copy under `npx shadcn@latest add @suaveplan/radio-group` keeps the cva configurations in a dedicated file. Theme overrides flow through `useThemedVariants("RadioGroup" | "RadioGroupItem")`.
|
||||
|
||||
The container cva is intentionally minimal (`grid gap-2` only) — the layout is a vertical stack with uniform gap. The item cva carries the visual recipe (border, focus ring, disabled decay) and the `size` axis.
|
||||
|
||||
## Features
|
||||
|
||||
- Container layout via `grid gap-2` (NOT `space-y-*` per the shadcn skill rule).
|
||||
- Item border `border-primary` + `text-primary` so the indicator inherits the colour automatically via `fill-current text-current`.
|
||||
- Three item sizes (`sm` → `h-3.5 w-3.5`, `default` → `h-4 w-4`, `lg` → `h-5 w-5`).
|
||||
- `disabled:cursor-not-allowed disabled:opacity-50` for the native disabled state.
|
||||
- `motion-reduce:transition-none` for users with `prefers-reduced-motion: reduce`.
|
||||
- `focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2` for keyboard-only focus styling.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { radioGroupVariants, radioGroupItemVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const containerClassName = radioGroupVariants();
|
||||
const itemClassName = radioGroupItemVariants({ size: "default" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { radioGroupItemVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomRadio() {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
role="radio"
|
||||
aria-checked="false"
|
||||
className={cn(
|
||||
radioGroupItemVariants({ size: "lg" }),
|
||||
"ring-2 ring-accent"
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `radioGroupVariants(): string`
|
||||
|
||||
Returns the container classes. Takes no arguments — the container cva exposes no axes.
|
||||
|
||||
### `radioGroupItemVariants(props?: { size?: "sm" | "default" | "lg" }): string`
|
||||
|
||||
Returns the item classes. Omitted size falls back to the cva `defaultVariants` (`size: "default"`).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The container uses `grid` (not `flex flex-col`) because grid collapses cross-axis layout in a way that keeps items column-aligned even when content lengths differ — the `<RadioGroupItem>` + label + description pattern stacks predictably.
|
||||
- The item cva does NOT include layout properties — consumers compose layout (item beside label) at the call-site so the chassis primitive stays composable.
|
||||
- Per the shadcn skill rule, the cva NEVER uses `space-y-*` / `space-x-*` — `gap-*` flows the same in RTL.
|
||||
163
docs/ui/react/react-ui/src/components/resizable/resizable.md
Normal file
163
docs/ui/react/react-ui/src/components/resizable/resizable.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# Resizable
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - resizable.variants — `resizablePanelGroupVariants`,…
|
||||
- [resizable.variants](./resizable.variants.md) — `resizablePanelGroupVariants`, `resizablePanelVariants`, `resizableHandleVariants`, and `resizableHandleGripVariants` a…
|
||||
- resizable.variants — `resizablePanelGroupVariants`,…
|
||||
|
||||
- [resizable.variants](./resizable.variants.md) — `resizablePanelGroupVariants`, `resizablePanelVariants`, `resizableHandleVariants`, and `resizableHandleGripVariants` a…
|
||||
|
||||
- resizable.variants — `resizablePanelGroupVariants`,…
|
||||
|
||||
- [resizable.variants](./resizable.variants.md) — `resizablePanelGroupVariants`, `resizablePanelVariants`, `resizableHandleVariants`, and `resizableHandleGripVariants` a…
|
||||
|
||||
- resizable.variants — `resizablePanelGroupVariants`,…
|
||||
|
||||
- [resizable.variants](./resizable.variants.md) — `resizablePanelGroupVariants`, `resizablePanelVariants`, `resizableHandleVariants`, and `resizableHandleGripVariants` a…
|
||||
- [resizable.variants](./resizable.variants.md) — `resizablePanelGroupVariants`, `resizablePanelVariants`, `resizableHandleVariants`, and `resizableHandleGripVariants` a…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<ResizablePanelGroup>`, `<ResizablePanel>`, and `<ResizableHandle>` are the chassis split-layout primitives built on `react-resizable-panels` v4 (`Group` / `Panel` / `Separator`). The library renders a flex container with `orientation` driving `flex-direction` (`horizontal` → row, `vertical` → column) and exposes pointer + keyboard resize gestures (Arrow keys when the handle is focused). The chassis layers cva slots and an optional `withHandle` indicator on the separator.
|
||||
|
||||
Use this primitive for split-layout surfaces (sidebar / main / preview, master-detail, code-editor + preview). Each panel sizes itself via flex-grow / flex-shrink driven by the library; consumers persist sizes via the `defaultLayout` / `onLayoutChanged` props that pass through to the library.
|
||||
|
||||
## Features
|
||||
|
||||
- Built on `react-resizable-panels` v4 (`Group` / `Panel` / `Separator`).
|
||||
- `orientation` prop on the group: `"horizontal"` (default; row layout) or `"vertical"` (column layout).
|
||||
- Pointer drag + keyboard resize (ArrowLeft/Right or ArrowUp/Down when the handle is focused) supplied by the library.
|
||||
- `defaultSize`, `minSize`, `maxSize`, `collapsible` per panel.
|
||||
- `withHandle` opt-in grip indicator on `<ResizableHandle>` so coarse pointers (touch) get a visible hit indicator.
|
||||
- `defaultLayout` + `onLayoutChanged` for persisting sizes between sessions.
|
||||
- Theme-driven cva overrides for `ResizablePanelGroup`, `ResizablePanel`, `ResizableHandle`, `ResizableHandleGrip`.
|
||||
- `data-disabled` on the handle when the group's `disabled` prop is true; the handle still mounts but the cursor and opacity collapse so users see the disabled affordance.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
ResizableHandle,
|
||||
ResizablePanel,
|
||||
ResizablePanelGroup,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function TwoPaneSplit() {
|
||||
return (
|
||||
<ResizablePanelGroup id="main-split" className="rounded border">
|
||||
<ResizablePanel id="sidebar" defaultSize={25} minSize={15}>
|
||||
<div className="p-3">Sidebar</div>
|
||||
</ResizablePanel>
|
||||
<ResizableHandle id="handle-1" withHandle />
|
||||
<ResizablePanel id="main" defaultSize={75} minSize={40}>
|
||||
<div className="p-3">Main content</div>
|
||||
</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
ResizableHandle,
|
||||
ResizablePanel,
|
||||
ResizablePanelGroup,
|
||||
} from "@suaveplan/react-ui";
|
||||
import type { Layout } from "react-resizable-panels";
|
||||
|
||||
const STORAGE_KEY = "editor.layout";
|
||||
|
||||
function loadLayout(): Layout | undefined {
|
||||
try {
|
||||
const raw = localStorage.getItem(STORAGE_KEY);
|
||||
return raw ? (JSON.parse(raw) as Layout) : undefined;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function saveLayout(layout: Layout): void {
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(layout));
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
export function EditorSplit() {
|
||||
return (
|
||||
<ResizablePanelGroup
|
||||
id="editor-split"
|
||||
defaultLayout={loadLayout()}
|
||||
onLayoutChanged={saveLayout}
|
||||
className="h-screen"
|
||||
>
|
||||
<ResizablePanel id="files" defaultSize={20} minSize={15} collapsible>
|
||||
<FileTree />
|
||||
</ResizablePanel>
|
||||
<ResizableHandle id="h1" withHandle />
|
||||
<ResizablePanel id="editor" defaultSize={50} minSize={30}>
|
||||
<CodeEditor />
|
||||
</ResizablePanel>
|
||||
<ResizableHandle id="h2" withHandle />
|
||||
<ResizablePanel id="preview" defaultSize={30} minSize={20}>
|
||||
<LivePreview />
|
||||
</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<ResizablePanelGroup>`
|
||||
|
||||
Wraps `Group` from `react-resizable-panels`.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `id` | `string \| number` | `useId()` | Unique identifier within the application. Falls back to React's `useId()` when omitted. |
|
||||
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Layout axis. |
|
||||
| `defaultLayout` | `Layout` (`{ [panelId]: number }`) | _none_ | Initial layout (percentages 0..100 keyed by panel id). |
|
||||
| `onLayoutChange` | `(layout: Layout) => void` | _none_ | Fires on every pointer-move during drag — debounce before persisting. |
|
||||
| `onLayoutChanged` | `(layout: Layout) => void` | _none_ | Fires once after the pointer is released — recommended for persistence. |
|
||||
| `disabled` | `boolean` | `false` | Disable resize across the entire group; the handle gets `data-disabled` and the cursor stays default. |
|
||||
| `disableCursor` | `boolean` | `false` | Disable the library's drag-cursor styling. |
|
||||
|
||||
### `<ResizablePanel>`
|
||||
|
||||
Wraps `Panel`.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `id` | `string \| number` | `useId()` | Unique identifier within the group. |
|
||||
| `defaultSize` | `number \| string` | _auto_ | Initial size as a percentage (`0..100`) or a CSS value. |
|
||||
| `minSize` | `number \| string` | _none_ | Minimum size during resize. |
|
||||
| `maxSize` | `number \| string` | _none_ | Maximum size during resize. |
|
||||
| `collapsible` | `boolean` | `false` | Allow the panel to collapse below `minSize` and snap to zero. |
|
||||
| `onResize` | `(size, id, prev) => void` | _none_ | Fires when the panel's size changes. |
|
||||
|
||||
### `<ResizableHandle>`
|
||||
|
||||
Wraps `Separator`.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `id` | `string \| number` | `useId()` | Unique identifier within the group. |
|
||||
| `withHandle` | `boolean` | `false` | Render a small grip square (using lucide's `<GripVerticalIcon>`) centred on the handle. |
|
||||
| `disabled` | `boolean` | `false` | Disable this individual handle. |
|
||||
| `disableDoubleClick` | `boolean` | `false` | Disable double-click-to-collapse on this handle. |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The library v4 names are `Group` / `Panel` / `Separator`; the chassis re-exports them as `<ResizablePanelGroup>` / `<ResizablePanel>` / `<ResizableHandle>` for naming parity with the upstream shadcn registry name and so the chassis surface reads naturally.
|
||||
- The library applies `display`, `flex-direction`, `flex-wrap`, and `overflow` styles inline on the group root and CANNOT be overridden via className — the chassis cva sticks to layout-neutral recipes (`h-full w-full`).
|
||||
- The chassis NEVER assigns a numeric `z-index` (per the shadcn skill rule). Stacking is implicit — the handle sits between two panels in document order.
|
||||
- Each panel renders as an OUTER `<div data-panel>` wrapping a chassis-className-styled INNER `<div>`. Tests inspect the inner element when asserting cva classes.
|
||||
- The handle exposes `aria-orientation` perpendicular to the resize axis (a horizontal group has a vertical separator), matching the WAI-ARIA separator pattern.
|
||||
- The `withHandle` grip uses lucide's `<GripVerticalIcon>`; consumers can render a custom indicator by composing their own children inside `<ResizableHandle>` instead of opting into `withHandle`.
|
||||
- The `disabled` group prop sets `data-disabled` on the handle; the cva collapses the cursor and reduces opacity so users see the disabled state without losing the focus ring.
|
||||
- Per the WAI-ARIA separator pattern, the handle is keyboard-focusable (`tabindex="0"`); ArrowLeft/Right or ArrowUp/Down resize the adjacent panels by 5% per keystroke (library default).
|
||||
@@ -0,0 +1,79 @@
|
||||
# Resizable Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`resizablePanelGroupVariants`, `resizablePanelVariants`, `resizableHandleVariants`, and `resizableHandleGripVariants` are the cva (class-variance-authority) configurations powering every `<ResizablePanelGroup>` / `<ResizablePanel>` / `<ResizableHandle>` slot. They are split from `resizable.tsx` into their own module so an ejected source copy under `npx shadcn@latest add @suaveplan/resizable` keeps the cva configurations in a dedicated file. Theme overrides flow through `useThemedVariants("Resizable<Slot>")`.
|
||||
|
||||
The library applies `display`, `flex-direction`, `flex-wrap`, and `overflow` inline on the group root and these styles cannot be overridden via className. The cva therefore sticks to layout-neutral recipes (`h-full w-full`). Each panel's cva goes on the INNER chassis-rendered element (the library wraps each panel in an outer `<div data-panel>`).
|
||||
|
||||
## Features
|
||||
|
||||
- Group: `h-full w-full` so the resizable container fills its parent.
|
||||
- Panel: `relative overflow-hidden` so a scrolling child clips to the panel bounds and the panel's own ring/border doesn't leak past the box.
|
||||
- Handle: `bg-border transition-colors` plus per-axis sizing via `data-[orientation=horizontal]:w-px` / `data-[orientation=vertical]:h-px`; cursor switches between `cursor-col-resize` and `cursor-row-resize` based on axis. `data-[disabled]:cursor-default data-[disabled]:opacity-60` for the disabled state.
|
||||
- Grip: `z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border` so the indicator sits on top of the handle line.
|
||||
- Focus-visible ring on the handle: `focus-visible:ring-2 focus-visible:ring-ring`.
|
||||
- `motion-reduce:transition-none` collapses the colour transition under `prefers-reduced-motion: reduce`.
|
||||
- NEVER carries a numeric `z-index` greater than `z-10` (the grip needs to sit above the rail line; everything else is implicit).
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
resizableHandleGripVariants,
|
||||
resizableHandleVariants,
|
||||
resizablePanelGroupVariants,
|
||||
resizablePanelVariants,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
const groupClassName = resizablePanelGroupVariants();
|
||||
const panelClassName = resizablePanelVariants();
|
||||
const handleClassName = resizableHandleVariants();
|
||||
const gripClassName = resizableHandleGripVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { resizableHandleVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomHandle() {
|
||||
return (
|
||||
<div
|
||||
role="separator"
|
||||
tabIndex={0}
|
||||
aria-orientation="vertical"
|
||||
className={cn(
|
||||
resizableHandleVariants(),
|
||||
"bg-primary"
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `resizablePanelGroupVariants(): string`
|
||||
|
||||
Returns the group container classes; takes no arguments.
|
||||
|
||||
### `resizablePanelVariants(): string`
|
||||
|
||||
Returns the per-panel inner-wrapper classes; takes no arguments.
|
||||
|
||||
### `resizableHandleVariants(): string`
|
||||
|
||||
Returns the handle separator classes (orientation-aware via `data-[orientation]` selectors); takes no arguments.
|
||||
|
||||
### `resizableHandleGripVariants(): string`
|
||||
|
||||
Returns the grip indicator classes; takes no arguments.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The library applies inline `display: flex`, `flex-direction`, `flex-wrap`, `overflow: hidden` to the group root. Any cva override of these styles is ignored — keep the cva at layout-neutral classes (`h-full w-full` + spacing).
|
||||
- The handle cva uses `data-[orientation=horizontal]:w-px` to render a 1-pixel rail along the resize boundary. Consumers wanting a thicker rail should override via `cn(...)` rather than editing the cva.
|
||||
- The grip indicator is the ONLY part of this cva family that uses a numeric z-index (`z-10`), and only because it must sit on top of the rail line. Per the shadcn skill rule, the rest of the family avoids numeric z-indexes.
|
||||
- The `motion-reduce:transition-none` rule prevents the colour transition on hover under `prefers-reduced-motion: reduce` so users with motion sensitivity see no animated state changes.
|
||||
- The handle's `data-disabled` selector mirrors the library's runtime attribute when the group `disabled` prop is true; the cva collapses the cursor to default and reduces opacity so users see the disabled affordance.
|
||||
132
docs/ui/react/react-ui/src/components/scroll-area/scroll-area.md
Normal file
132
docs/ui/react/react-ui/src/components/scroll-area/scroll-area.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# ScrollArea
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - scroll-area.variants — `scrollAreaVariants`, `scrol…
|
||||
- [scroll-area.variants](./scroll-area.variants.md) — `scrollAreaVariants`, `scrollAreaViewportVariants`, `scrollAreaScrollbarVariants`, `scrollAreaThumbVariants`, and `scro…
|
||||
- scroll-area.variants — `scrollAreaVariants`, `scrol…
|
||||
|
||||
- [scroll-area.variants](./scroll-area.variants.md) — `scrollAreaVariants`, `scrollAreaViewportVariants`, `scrollAreaScrollbarVariants`, `scrollAreaThumbVariants`, and `scro…
|
||||
|
||||
- scroll-area.variants — `scrollAreaVariants`, `scrol…
|
||||
|
||||
- [scroll-area.variants](./scroll-area.variants.md) — `scrollAreaVariants`, `scrollAreaViewportVariants`, `scrollAreaScrollbarVariants`, `scrollAreaThumbVariants`, and `scro…
|
||||
|
||||
- scroll-area.variants — `scrollAreaVariants`, `scrol…
|
||||
|
||||
- [scroll-area.variants](./scroll-area.variants.md) — `scrollAreaVariants`, `scrollAreaViewportVariants`, `scrollAreaScrollbarVariants`, `scrollAreaThumbVariants`, and `scro…
|
||||
- [scroll-area.variants](./scroll-area.variants.md) — `scrollAreaVariants`, `scrollAreaViewportVariants`, `scrollAreaScrollbarVariants`, `scrollAreaThumbVariants`, and `scro…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<ScrollArea>`, `<ScrollAreaViewport>`, `<ScrollAreaScrollbar>`, `<ScrollAreaThumb>`, and `<ScrollAreaCorner>` are the chassis cross-platform custom-scrollbar primitives built on `@radix-ui/react-scroll-area`. Radix supplies the cross-platform polyfill: it observes the viewport's scroll state, computes thumb sizes, and routes pointer / wheel / keyboard events back into native scrolling so consumers get a consistent visual scrollbar that respects native scroll semantics on every browser.
|
||||
|
||||
The chassis exposes a convenience `orientation` prop on the Root — `"vertical"` (default) renders one vertical rail, `"horizontal"` renders one horizontal rail, `"both"` renders both rails plus the corner element. Consumers needing finer control over the composition can render `<ScrollAreaScrollbar>` and `<ScrollAreaCorner>` directly inside their own `<ScrollArea>` tree.
|
||||
|
||||
## Features
|
||||
|
||||
- Cross-browser scrollbar polyfill via Radix; native scrolling preserved (wheel, touch, keyboard, focus-on-scroll).
|
||||
- `orientation` shorthand: `"vertical"` (default) / `"horizontal"` / `"both"` decides which rails the chassis Root mounts.
|
||||
- Per-rail composition: `<ScrollAreaScrollbar orientation="vertical" | "horizontal">` plus `<ScrollAreaThumb>` for custom layouts.
|
||||
- `<ScrollAreaCorner>` square at the intersection when both rails are visible.
|
||||
- `type` prop forwarded to Radix for visibility behaviour: `"auto"` (default), `"always"` (rail always mounted; useful for tests under happy-dom), `"scroll"` (only while scrolling), `"hover"` (only while hovering).
|
||||
- `scrollHideDelay` prop forwarded to Radix to tune the auto-hide delay (ms).
|
||||
- Theme-driven cva overrides for `ScrollArea`, `ScrollAreaViewport`, `ScrollAreaScrollbar`, `ScrollAreaThumb`, `ScrollAreaCorner`.
|
||||
- All sub-components forward refs to their underlying Radix elements.
|
||||
- Logical-property layout (`select-none`, `touch-none`) prevents text selection and double-tap zoom while dragging the thumb.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { ScrollArea } from "@suaveplan/react-ui";
|
||||
|
||||
export function NotesList({ notes }: { notes: ReadonlyArray<string> }) {
|
||||
return (
|
||||
<ScrollArea className="h-72 w-48 rounded-md border p-3">
|
||||
<ul className="flex flex-col gap-2">
|
||||
{notes.map((note, idx) => (
|
||||
<li key={`${idx}-${note}`}>{note}</li>
|
||||
))}
|
||||
</ul>
|
||||
</ScrollArea>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
ScrollArea,
|
||||
ScrollAreaCorner,
|
||||
ScrollAreaScrollbar,
|
||||
ScrollAreaThumb,
|
||||
ScrollAreaViewport,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function CodeBlock({ code }: { code: string }) {
|
||||
return (
|
||||
<ScrollArea
|
||||
type="hover"
|
||||
scrollHideDelay={400}
|
||||
className="max-h-72 w-full rounded border bg-muted p-3"
|
||||
>
|
||||
<ScrollAreaViewport>
|
||||
<pre className="text-xs leading-tight">
|
||||
<code>{code}</code>
|
||||
</pre>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar orientation="vertical">
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
<ScrollAreaScrollbar orientation="horizontal">
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
<ScrollAreaCorner />
|
||||
</ScrollArea>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<ScrollArea>`
|
||||
|
||||
Wraps the Radix Root. Most consumers pass `className` to size the area (e.g. `h-72 w-48`).
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `orientation` | `"vertical" \| "horizontal" \| "both"` | `"vertical"` | Selects which rails the chassis Root mounts. |
|
||||
| `type` | `"auto" \| "always" \| "scroll" \| "hover"` | `"hover"` (Radix default) | Visibility behaviour for the rails. Use `"always"` for tests under happy-dom. |
|
||||
| `scrollHideDelay` | `number` | `600` | Delay in ms before the rails auto-hide. |
|
||||
| `dir` | `"ltr" \| "rtl"` | inherited | Reading direction; vertical rails sit on the end side. |
|
||||
|
||||
### `<ScrollAreaViewport>`
|
||||
|
||||
The actual scrolling element. `<ScrollArea>` wraps its children in this automatically; render directly only when composing custom siblings.
|
||||
|
||||
### `<ScrollAreaScrollbar>`
|
||||
|
||||
A single rail. `orientation` selects horizontal vs vertical layout via a `data-orientation` attribute.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `orientation` | `"horizontal" \| "vertical"` | `"vertical"` | Rail axis. |
|
||||
| `forceMount` | `true` | _undefined_ | Forces the rail into the DOM regardless of Radix's mount-on-need behaviour. |
|
||||
|
||||
### `<ScrollAreaThumb>`
|
||||
|
||||
The draggable rail-rider. Sits inside a `<ScrollAreaScrollbar>`.
|
||||
|
||||
### `<ScrollAreaCorner>`
|
||||
|
||||
The corner square between horizontal and vertical rails. Only mounted when both rails are visible.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis NEVER assigns a numeric `z-index` (per the shadcn skill rule); the rails sit on top of the viewport via Radix's portalled overlay positioning.
|
||||
- Radix's `type="hover"` (its default) mounts the rail in the DOM only when the user hovers. happy-dom cannot compute layout dimensions, so tests that assert rail presence must pass `type="always"` so the rail mounts unconditionally (per Wave 3c finding #2 — third-party gesture-based components may not test well under happy-dom).
|
||||
- Drag-the-thumb gestures are not exercised in the chassis test suite — happy-dom does not fire pointercapture / pointermove events accurately enough to drive the thumb. Visual confirmation lives in Storybook.
|
||||
- The viewport cva uses `[&>div]:!block` to flatten Radix's inner table-cell hack so consumer flex / grid layouts behave naturally.
|
||||
- The thumb cva includes a `before:` pseudo-element with `min-h-[44px] min-w-[44px]` so the touch-target meets WCAG 2.5.5 (Target Size) regardless of the visual thumb height.
|
||||
- Per WAI-ARIA, the scrollable region SHOULD be exposed as such via `role="region"` plus an accessible name. Radix does not add this automatically — consumers wanting screen-reader narration should wrap the `<ScrollArea>` content in a `<section aria-label="...">` or carry the aria attributes on the `<ScrollAreaViewport>` (which forwards arbitrary props to the rendered `<div>`).
|
||||
@@ -0,0 +1,79 @@
|
||||
# ScrollArea Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`scrollAreaVariants`, `scrollAreaViewportVariants`, `scrollAreaScrollbarVariants`, `scrollAreaThumbVariants`, and `scrollAreaCornerVariants` are the cva (class-variance-authority) configurations powering every `<ScrollArea>` slot. They are split from `scroll-area.tsx` into their own module so an ejected source copy under `npx shadcn@latest add @suaveplan/scroll-area` keeps the cva configurations in a dedicated file. Theme overrides flow through `useThemedVariants("ScrollArea<Slot>")`.
|
||||
|
||||
The root sets up the position context and clips overflow so the rails sit cleanly on top of the viewport. The viewport scrolls (and flattens Radix's inner table-cell hack). The scrollbar carries `data-[orientation=vertical|horizontal]` selectors so the rail dimensions and border edges differ per axis. The thumb is a rounded muted-foreground bar with a 44×44 touch-target pseudo-element (WCAG 2.5.5).
|
||||
|
||||
## Features
|
||||
|
||||
- Root: `relative overflow-hidden` so the rails sit on top of the viewport without leaking past the box.
|
||||
- Viewport: `h-full w-full rounded-[inherit] [&>div]:!block` so consumer layouts behave naturally without Radix's table-cell hack interfering.
|
||||
- Scrollbar: per-axis sizing via `data-[orientation=vertical]` (full-height, narrow rail with a left-border padding) vs `data-[orientation=horizontal]` (narrow row with a top-border padding); `touch-none select-none` so dragging the thumb does not select text or trigger double-tap zoom.
|
||||
- Thumb: rounded muted-foreground bar with a `min-h-[44px] min-w-[44px]` `before:` pseudo so the touch-target meets WCAG 2.5.5 (Target Size).
|
||||
- Corner: transparent square so it does not visually interrupt the viewport.
|
||||
- NEVER carries a numeric `z-index` (Radix's overlay positioning handles stacking).
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
scrollAreaScrollbarVariants,
|
||||
scrollAreaThumbVariants,
|
||||
scrollAreaVariants,
|
||||
scrollAreaViewportVariants,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
const rootClassName = scrollAreaVariants();
|
||||
const viewportClassName = scrollAreaViewportVariants();
|
||||
const scrollbarClassName = scrollAreaScrollbarVariants();
|
||||
const thumbClassName = scrollAreaThumbVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { scrollAreaThumbVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomThumb() {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
scrollAreaThumbVariants(),
|
||||
"bg-primary"
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `scrollAreaVariants(): string`
|
||||
|
||||
Returns the root grouping classes; takes no arguments.
|
||||
|
||||
### `scrollAreaViewportVariants(): string`
|
||||
|
||||
Returns the viewport classes; takes no arguments.
|
||||
|
||||
### `scrollAreaScrollbarVariants(): string`
|
||||
|
||||
Returns the scrollbar rail classes (orientation-aware via `data-orientation` selectors); takes no arguments.
|
||||
|
||||
### `scrollAreaThumbVariants(): string`
|
||||
|
||||
Returns the thumb classes; takes no arguments.
|
||||
|
||||
### `scrollAreaCornerVariants(): string`
|
||||
|
||||
Returns the corner classes; takes no arguments.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The viewport's `[&>div]:!block` selector overrides Radix's inner `display: table-cell` element. Without it, consumer flex / grid layouts break because the table-cell wrapper interferes with their box-model assumptions.
|
||||
- The scrollbar's per-axis sizing is implemented with `data-[orientation=vertical|horizontal]` Tailwind variants so a single cva covers both axes. Theme overrides may swap the entire recipe.
|
||||
- The thumb's `before:` pseudo-element provides a 44×44 hit-target without changing the visible thumb height — the visible thumb stays slim while the hit area meets WCAG 2.5.5 (Target Size).
|
||||
- Per the shadcn skill rule, the cva NEVER assigns a numeric `z-index`. The rails sit on top of the viewport via Radix's overlay positioning, not via stacking-order assertions.
|
||||
- The `motion-reduce:transition-none` rule on the scrollbar collapses the colour transition under `prefers-reduced-motion: reduce` so users with motion sensitivity see no animated state changes on hover.
|
||||
154
docs/ui/react/react-ui/src/components/select/select.md
Normal file
154
docs/ui/react/react-ui/src/components/select/select.md
Normal file
@@ -0,0 +1,154 @@
|
||||
# Select
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - select.test-fixtures — Wave-3h local test fixture t…
|
||||
- [select.test-fixtures](./select.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.select.placeholder` key. The…
|
||||
- [select.variants](./select.variants.md) — The `<Select>` component is composed of six visual slots — trigger, content panel, item row, group label, separator, an…
|
||||
- select.test-fixtures — Wave-3h local test fixture t…
|
||||
|
||||
- [select.test-fixtures](./select.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.select.placeholder` key. The…
|
||||
- [select.variants](./select.variants.md) — The `<Select>` component is composed of six visual slots — trigger, content panel, item row, group label, separator, an…
|
||||
|
||||
- select.test-fixtures — Wave-3h local test fixture t…
|
||||
|
||||
- [select.test-fixtures](./select.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.select.placeholder` key. The…
|
||||
- [select.variants](./select.variants.md) — The `<Select>` component is composed of six visual slots — trigger, content panel, item row, group label, separator, an…
|
||||
|
||||
- select.test-fixtures — Wave-3h local test fixture t…
|
||||
|
||||
- [select.test-fixtures](./select.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.select.placeholder` key. The…
|
||||
- [select.variants](./select.variants.md) — The `<Select>` component is composed of six visual slots — trigger, content panel, item row, group label, separator, an…
|
||||
- [select.test-fixtures](./select.test-fixtures.md) — Wave-3h local test fixture that augments the chassis test i18n instance with the `react-ui.select.placeholder` key. The…
|
||||
- [select.variants](./select.variants.md) — The `<Select>` component is composed of six visual slots — trigger, content panel, item row, group label, separator, an…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Select>` and its sub-components (`<SelectTrigger>`, `<SelectValue>`, `<SelectContent>`, `<SelectItem>`, `<SelectGroup>`, `<SelectLabel>`, `<SelectSeparator>`, `<SelectScrollUpButton>`, `<SelectScrollDownButton>`, `<SelectPortal>`, `<SelectViewport>`) are the chassis single-value picker built on `@radix-ui/react-select`. Use this for short, fully-known option sets (status, priority, country in a typed form) where the user does not need to type-to-filter and there is no async source.
|
||||
|
||||
Per Wave 3a's reconciliation finding #2: Select ships discrete from `<Combobox>`. They share visual styling but the APIs differ — Radix Select narrates `role="combobox"` semantics with a fixed option list and a popover panel; Combobox layers on cmdk's filtering, async loading, and multi-select. When the option set is dynamic, filterable, async, or supports multi-select, reach for `<Combobox>`, `<MultiSelect>`, or `<SearchableSelect>` instead.
|
||||
|
||||
Per the shadcn skill rule: **every `<SelectItem>` MUST live inside a `<SelectGroup>`**. The chassis does not enforce this at runtime — Radix accepts orphan items — but the rule keeps screen-reader navigation predictable and matches the upstream block convention.
|
||||
|
||||
## Features
|
||||
|
||||
- Radix-driven a11y: `role="combobox"` on the trigger, `role="listbox"` on the panel, `aria-activedescendant` for keyboard navigation, `aria-selected` per item, native `Up`/`Down`/`Home`/`End`/`Enter`/`Escape` keyboard support.
|
||||
- Self-portals via `<SelectPortal>` so consumers do not compose the portal manually.
|
||||
- Click-outside dismissal + Escape-to-close.
|
||||
- `placeholderKey` on `<SelectValue>` typed `TranslationKey` — resolved via `useTranslation()` at render time.
|
||||
- Lucide-iconography for the trigger chevron + per-item check + scroll-up/down buttons.
|
||||
- Theme-driven cva via `useThemedVariants()` for every visual slot (trigger, content, item, label, separator, scroll button).
|
||||
- All sub-components forward refs.
|
||||
- `motion-reduce:animate-none` collapses panel open/close animations under `prefers-reduced-motion: reduce`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function PriorityPicker() {
|
||||
return (
|
||||
<Select defaultValue="medium">
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholderKey="react-ui.select.placeholder" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="low">Low</SelectItem>
|
||||
<SelectItem value="medium">Medium</SelectItem>
|
||||
<SelectItem value="high">High</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectSeparator,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@suaveplan/react-ui";
|
||||
import { useState } from "react";
|
||||
|
||||
export function CountryPicker() {
|
||||
const [country, setCountry] = useState<string | undefined>();
|
||||
return (
|
||||
<Select value={country} onValueChange={setCountry}>
|
||||
<SelectTrigger className="w-64">
|
||||
<SelectValue placeholderKey="react-ui.select.placeholder" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>Europe</SelectLabel>
|
||||
<SelectItem value="gb">United Kingdom</SelectItem>
|
||||
<SelectItem value="de">Germany</SelectItem>
|
||||
<SelectItem value="fr">France</SelectItem>
|
||||
</SelectGroup>
|
||||
<SelectSeparator />
|
||||
<SelectGroup>
|
||||
<SelectLabel>Americas</SelectLabel>
|
||||
<SelectItem value="us">United States</SelectItem>
|
||||
<SelectItem value="ca">Canada</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Select>`
|
||||
|
||||
Wraps the Radix Select Root. Same `value` / `defaultValue` / `onValueChange` / `open` / `onOpenChange` / `disabled` API as the upstream.
|
||||
|
||||
### `<SelectTrigger>`
|
||||
|
||||
The visual button that opens the popover. Renders a chevron-down icon at the trailing edge. Forwards refs to the underlying `<button>`.
|
||||
|
||||
### `<SelectValue>`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `placeholderKey` | `TranslationKey` | — | Translation key resolved at render time. Branded so consumers cannot pass raw strings. |
|
||||
| `placeholder` | `string` | — | Raw placeholder string — accepted as a back-compat escape hatch when the consumer doesn't want translation routing. |
|
||||
|
||||
### `<SelectContent>`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `position` | `"item-aligned" \| "popper"` | `"popper"` | Positioning strategy. |
|
||||
| `sideOffset` | `number` | `4` | Numeric offset along the side axis when `position="popper"`. |
|
||||
| _every other_ Radix Content prop | _upstream_ | _upstream_ | Forwarded as-is. |
|
||||
|
||||
### `<SelectItem>`
|
||||
|
||||
Single selectable row. Children become the rendered item text. The chassis renders a check mark via `<SelectPrimitive.ItemIndicator>` on the leading edge when selected.
|
||||
|
||||
### `<SelectGroup>`, `<SelectLabel>`, `<SelectSeparator>`, `<SelectScrollUpButton>`, `<SelectScrollDownButton>`, `<SelectPortal>`, `<SelectViewport>`
|
||||
|
||||
Direct re-exports of the Radix sub-components with the chassis cva applied where appropriate. See <https://www.radix-ui.com/primitives/docs/components/select> for the upstream API.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis NEVER assigns a numeric `z-index` — Radix portals into the body so stacking is implicit (the `z-50` in `selectContentVariants` is a stacking context within the portal, not a numeric global).
|
||||
- `position="popper"` is the chassis default (matching shadcn upstream) — switch to `"item-aligned"` only when the consumer needs the active item to vertically align with the trigger.
|
||||
- `<SelectContent>` self-portals via `<SelectPortal>` AND self-wraps children in `<SelectPrimitive.Viewport>` so consumers do not compose either manually. The standalone `<SelectViewport>` export is provided for advanced custom-content scenarios.
|
||||
- The check-mark indicator uses `data-icon="check"` so theme icon-pack overrides can swap it.
|
||||
- For modal selection surfaces (e.g. mobile bottom sheet), use `<Drawer>` instead — Radix Select is not a modal primitive.
|
||||
@@ -0,0 +1,63 @@
|
||||
# select.test-fixtures
|
||||
|
||||
## Purpose
|
||||
|
||||
Wave-3h local test fixture that augments the chassis test i18n instance with
|
||||
the `react-ui.select.placeholder` key. The chassis spec tests under
|
||||
`select.test.tsx` assert on the resolved English string ("Select an option")
|
||||
before the central wireup commit folds the canonical translation into the
|
||||
bundled `en-GB.json` source-of-truth. Once the wireup commit lands, the
|
||||
bundled key shadows the fixture and `augmentI18nWithSelectKeys` becomes a
|
||||
no-op re-merge — the fixture stays in place during the deletion window so
|
||||
the deletion can land as a single atomic cleanup pass without breaking the
|
||||
suite mid-rebase.
|
||||
|
||||
## Features
|
||||
|
||||
- Bundles the localized `select.placeholder` string for all five
|
||||
chassis-supported locales (`en-GB`, `zh-CN`, `ar-AE`, `cy-GB`, `ru-RU`)
|
||||
- Idempotent merge — calling the augment function twice against the same
|
||||
instance is a no-op
|
||||
- Zero coupling to the call-site structure: the augment helper accepts any
|
||||
`I18nInstance`
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
|
||||
```ts
|
||||
import { createTestI18n } from "../../../tests/setup/test-i18n.js";
|
||||
import { augmentI18nWithSelectKeys } from "./select.test-fixtures.js";
|
||||
|
||||
const i18n = await createTestI18n({ locale: "en-GB" });
|
||||
augmentI18nWithSelectKeys(i18n);
|
||||
```
|
||||
|
||||
### Advanced Example
|
||||
|
||||
```ts
|
||||
import { createTestI18n } from "../../../tests/setup/test-i18n.js";
|
||||
import { augmentI18nWithSelectKeys } from "./select.test-fixtures.js";
|
||||
|
||||
for (const locale of ["en-GB", "ar-AE", "cy-GB"] as const) {
|
||||
const i18n = await createTestI18n({ locale });
|
||||
augmentI18nWithSelectKeys(i18n);
|
||||
// Assert against the locale-specific resolved string.
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `augmentI18nWithSelectKeys(i18n: I18nInstance): void`
|
||||
|
||||
Merges the bundled `select` keyset into the supplied i18n instance for every
|
||||
supported locale. Returns nothing. Idempotent.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The fixture is intentionally tiny — five locale × one-key bundles plus a
|
||||
loop. Once the wireup commit folds the canonical key into the chassis
|
||||
`en-GB.json` source-of-truth, this file will be removed alongside its `.md`
|
||||
sibling, the spec test imports rewritten to drop the
|
||||
`augmentI18nWithSelectKeys` call, and the change archived as part of the
|
||||
Wave 3h cleanup pass.
|
||||
@@ -0,0 +1,46 @@
|
||||
# Select Variants
|
||||
|
||||
## Purpose
|
||||
|
||||
The `<Select>` component is composed of six visual slots — trigger, content panel, item row, group label, separator, and scroll button — each backed by a discrete cva module so theme overrides can target individual surfaces without touching the implementation file. Mirrors the upstream shadcn Select recipe with the chassis adding `useThemedVariants` resolution at every slot so a `ThemePlugin` may declare `variantOverrides: { SelectTrigger: <override>, SelectItem: <override>, … }`.
|
||||
|
||||
## Features
|
||||
|
||||
- Six discrete cva resolvers — `selectTriggerVariants`, `selectContentVariants`, `selectItemVariants`, `selectLabelVariants`, `selectSeparatorVariants`, `selectScrollButtonVariants`.
|
||||
- `selectTriggerVariants` mirrors the chassis `<Input>` recipe so a Select trigger renders flush with adjacent inputs in a form row.
|
||||
- `selectContentVariants` includes the same animation block as `popoverContentVariants` so panel open/close animations agree with the rest of the floating-panel family.
|
||||
- `selectItemVariants` reserves `pl-8` for the leading check-mark indicator so the item text aligns regardless of selection state.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
selectContentVariants,
|
||||
selectItemVariants,
|
||||
selectTriggerVariants,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
const triggerClasses = selectTriggerVariants();
|
||||
const contentClasses = selectContentVariants();
|
||||
const itemClasses = selectItemVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { cn, selectTriggerVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const compactTrigger = cn(selectTriggerVariants(), "h-7 text-xs px-2 py-0");
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
Each resolver returns the merged class string. None of the cva modules expose variant axes — the visual recipes are single-rung mirrors of the upstream shadcn block. To extend, declare a `variantOverrides` map in your `ThemePlugin`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `selectContentVariants` carries `z-50` — this is a stacking context within the Radix portal, NOT a numeric global z-index. Radix portals into the document body so the panel always renders above the trigger regardless of the surrounding CSS containment.
|
||||
- `selectItemVariants` uses `pl-8` (not `pl-2`) so the check-mark indicator slot is reserved AT ALL TIMES — toggling the indicator should never reflow the item text.
|
||||
- The animation block in `selectContentVariants` matches `popoverContentVariants` and `tooltipContentVariants` so floating-panel motion feels coherent across the chassis.
|
||||
- `motion-reduce:animate-none` collapses the open/close animations under `prefers-reduced-motion: reduce`.
|
||||
- `selectScrollButtonVariants` is shared between `<SelectScrollUpButton>` and `<SelectScrollDownButton>` — the only visual difference between the two is the chevron icon, which lives in the implementation file.
|
||||
108
docs/ui/react/react-ui/src/components/separator/separator.md
Normal file
108
docs/ui/react/react-ui/src/components/separator/separator.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# Separator
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - separator.variants — `separatorVariants` is the cva…
|
||||
- [separator.variants](./separator.variants.md) — `separatorVariants` is the cva (class-variance-authority) configuration that powers every `<Separator>` render. It is s…
|
||||
- separator.variants — `separatorVariants` is the cva…
|
||||
|
||||
- [separator.variants](./separator.variants.md) — `separatorVariants` is the cva (class-variance-authority) configuration that powers every `<Separator>` render. It is s…
|
||||
|
||||
- separator.variants — `separatorVariants` is the cva…
|
||||
|
||||
- [separator.variants](./separator.variants.md) — `separatorVariants` is the cva (class-variance-authority) configuration that powers every `<Separator>` render. It is s…
|
||||
|
||||
- separator.variants — `separatorVariants` is the cva…
|
||||
|
||||
- [separator.variants](./separator.variants.md) — `separatorVariants` is the cva (class-variance-authority) configuration that powers every `<Separator>` render. It is s…
|
||||
- [separator.variants](./separator.variants.md) — `separatorVariants` is the cva (class-variance-authority) configuration that powers every `<Separator>` render. It is s…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Separator>` is the chassis horizontal/vertical divider primitive for `@suaveplan/react-ui`. It wraps `@radix-ui/react-separator` so consumers inherit the Radix a11y semantics (`role="separator"` + `aria-orientation` for non-decorative use, `role="none"` for decorative use) while threading variant resolution through `useThemedVariants("Separator")` so themes may extend the orientation tokens (e.g. add a `dashed` variant).
|
||||
|
||||
The component carries no internal text and no translation lookup. Its only purpose is to draw a visual / semantic divider; consumers compose surrounding content as they see fit.
|
||||
|
||||
## Features
|
||||
|
||||
- Wraps `@radix-ui/react-separator` for full a11y semantics:
|
||||
- `decorative={true}` (the chassis default) → `role="none"`
|
||||
- `decorative={false}` → `role="separator"` + `aria-orientation` mirroring the `orientation` prop
|
||||
- Two orientations: `"horizontal"` (default) renders a width-100% / height-1 strip; `"vertical"` renders a width-1 / height-100% strip.
|
||||
- Forwards refs to the underlying `<div>` (Radix `Separator.Root` is implemented as a `<div>`).
|
||||
- All native `HTMLAttributes` are forwarded as-is via the Radix primitive's props passthrough.
|
||||
- Theme variant overrides via `useThemedVariants("Separator", separatorVariants)`. A `ThemePlugin.variantOverrides.Separator` may add brand-new orientation variants or visual variants.
|
||||
- No internal text; no `useTranslation` interaction; no provider dependency for translation.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { Separator } from "@suaveplan/react-ui";
|
||||
|
||||
export function ProfileBlock() {
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<h3>Account</h3>
|
||||
<p>Manage your account preferences here.</p>
|
||||
<Separator />
|
||||
<h3>Billing</h3>
|
||||
<p>Manage your billing preferences here.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { Separator } from "@suaveplan/react-ui";
|
||||
|
||||
export function NavRail() {
|
||||
return (
|
||||
<div className="flex h-12 items-center gap-3">
|
||||
<a href="/" className="text-sm">
|
||||
Home
|
||||
</a>
|
||||
<Separator orientation="vertical" decorative={false} />
|
||||
<a href="/profile" className="text-sm">
|
||||
Profile
|
||||
</a>
|
||||
<Separator orientation="vertical" decorative={false} />
|
||||
<a href="/settings" className="text-sm">
|
||||
Settings
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Separator>`
|
||||
|
||||
`forwardRef<HTMLDivElement, SeparatorProps>` — renders a Radix `<Separator.Root>` element with the active theme's variant resolver applied.
|
||||
|
||||
#### `SeparatorProps`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Drives both the cva variant (visual width/height) AND the Radix `aria-orientation`. |
|
||||
| `decorative` | `boolean` | `true` | When `true`, Radix sets `role="none"` so AT skips the separator. When `false`, Radix sets `role="separator"` and announces the orientation. |
|
||||
| _all_ `ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>` | _native_ | _native_ | Forwarded as-is to Radix. |
|
||||
|
||||
#### `separatorVariants`
|
||||
|
||||
The bare cva resolver. Exported so external consumers (`@suaveplan/react-ui-theme-*`, ejected source under `npx shadcn@latest add @suaveplan/separator`) can re-use the variants directly without going through `useThemedVariants`.
|
||||
|
||||
#### Errors
|
||||
|
||||
The Separator component does NOT call `useTranslation()` so it does not interact with the i18n provider at all. It is safe to render outside a `<TranslationProvider>` / `<ReactUiProvider>`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis re-exports Radix's `Separator.Root` rather than re-implementing the a11y semantics. The decorative-vs-semantic distinction is non-trivial to get right and Radix is the upstream-of-record.
|
||||
- The cva config's `orientation` variant default (`"horizontal"`) and the Radix primitive's own default both align — the chassis passes `orientation` through explicitly to Radix so the visual orientation and the AT-announced orientation can never drift apart.
|
||||
- The variants module is intentionally separate (`separator.variants.ts`) so an ejected source copy keeps the cva config in a dedicated file, mirroring the upstream shadcn convention.
|
||||
- `useThemedVariants("Separator", separatorVariants)` is called unconditionally so the hook order is stable across renders. The hook's memoisation key is `(themeName, "Separator")` so a re-render with the same theme returns the same resolver reference.
|
||||
- `bg-border` is the cva-applied colour; the actual border-colour value comes from the active theme's `cssVariables.border` token.
|
||||
- The chassis does not expose a `thickness` variant — consumers wanting a thicker divider override via `className="h-[3px]"` (horizontal) or `className="w-[3px]"` (vertical) and the cva config's `h-px` / `w-px` defaults are merged-out by `tailwind-merge` in `cn()`.
|
||||
@@ -0,0 +1,54 @@
|
||||
# Separator Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`separatorVariants` is the cva (class-variance-authority) configuration that powers every `<Separator>` render. It is split from `separator.tsx` into its own module so an ejected source copy under `npx shadcn@latest add @suaveplan/separator` keeps the cva configuration in a dedicated file — exactly mirroring the upstream shadcn convention. Theme overrides flow through `useThemedVariants("Separator", separatorVariants)`, so the runtime cva resolver can be replaced or extended by an active `ThemePlugin` without touching the component source.
|
||||
|
||||
The module exports a single `separatorVariants` callable resolver (`(props?) => string`) tagged with the component name `"Separator"` via `defineVariants`. Consumers that want the bare resolver — for example to render the same divider styling on a non-Radix element — import `separatorVariants` directly and apply the result via `cn()` themselves.
|
||||
|
||||
## Features
|
||||
|
||||
- Two orientations (`horizontal` / `vertical`) — `horizontal` collapses to `h-px w-full`, `vertical` to `h-full w-px`.
|
||||
- `shrink-0` so the separator never collapses when placed inside a flex / grid container.
|
||||
- `bg-border` so the colour comes from the active theme's `border` token.
|
||||
- Default orientation is `horizontal` so `separatorVariants()` (no props) produces the chassis horizontal separator.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { separatorVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const className = separatorVariants({ orientation: "horizontal" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { separatorVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomVerticalRule({ className: extra }: { className?: string }) {
|
||||
return (
|
||||
<span
|
||||
role="separator"
|
||||
aria-orientation="vertical"
|
||||
className={cn(
|
||||
separatorVariants({ orientation: "vertical" }),
|
||||
"mx-2 inline-block h-4",
|
||||
extra
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `separatorVariants(props?: VariantProps<typeof separatorVariants>): string`
|
||||
|
||||
Returns the cva-merged class string for the supplied orientation. The `orientation` field is optional; an omitted field falls back to the cva `defaultVariants.orientation: "horizontal"`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The resolver is tagged with `componentName: "Separator"` (non-enumerable, non-writable) via `defineVariants` so debug tooling can identify the cva instance at runtime.
|
||||
- The chassis does NOT expose a `thickness` variant — consumers wanting a thicker divider override via `className="h-[3px]"` (horizontal) or `className="w-[3px]"` (vertical) and the cva config's `h-px` / `w-px` defaults are merged-out by `tailwind-merge` in `cn()`.
|
||||
- The cva config is intentionally minimal — variants are added by themes via `useThemedVariants("Separator", separatorVariants)` rather than baked into the chassis surface (a `dashed` variant, a `gradient` variant, etc. are all theme territory).
|
||||
146
docs/ui/react/react-ui/src/components/sheet/sheet.md
Normal file
146
docs/ui/react/react-ui/src/components/sheet/sheet.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# Sheet
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - sheet.variants — `sheetOverlayVariants`, `sheetCont…
|
||||
- [sheet.variants](./sheet.variants.md) — `sheetOverlayVariants`, `sheetContentVariants`, `sheetHeaderVariants`, `sheetFooterVariants`, `sheetTitleVariants`, `sh…
|
||||
- sheet.variants — `sheetOverlayVariants`, `sheetCont…
|
||||
|
||||
- [sheet.variants](./sheet.variants.md) — `sheetOverlayVariants`, `sheetContentVariants`, `sheetHeaderVariants`, `sheetFooterVariants`, `sheetTitleVariants`, `sh…
|
||||
|
||||
- sheet.variants — `sheetOverlayVariants`, `sheetCont…
|
||||
|
||||
- [sheet.variants](./sheet.variants.md) — `sheetOverlayVariants`, `sheetContentVariants`, `sheetHeaderVariants`, `sheetFooterVariants`, `sheetTitleVariants`, `sh…
|
||||
|
||||
- sheet.variants — `sheetOverlayVariants`, `sheetCont…
|
||||
|
||||
- [sheet.variants](./sheet.variants.md) — `sheetOverlayVariants`, `sheetContentVariants`, `sheetHeaderVariants`, `sheetFooterVariants`, `sheetTitleVariants`, `sh…
|
||||
- [sheet.variants](./sheet.variants.md) — `sheetOverlayVariants`, `sheetContentVariants`, `sheetHeaderVariants`, `sheetFooterVariants`, `sheetTitleVariants`, `sh…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Sheet>` and its sub-components (`<SheetTrigger>`, `<SheetPortal>`, `<SheetOverlay>`, `<SheetContent>`, `<SheetHeader>`, `<SheetFooter>`, `<SheetTitle>`, `<SheetDescription>`, `<SheetClose>`) are the chassis side-anchored panel primitives built on `@radix-ui/react-dialog`. Sheet is structurally a Dialog with side-anchored positioning, so Radix supplies the same WAI-ARIA dialog semantics (focus-trap, restore-focus, Escape closes, click-outside closes) and the chassis adds a `side` axis on `<SheetContent>` (`top` / `right` / `bottom` / `left`).
|
||||
|
||||
Per the WAI-ARIA dialog pattern, every sheet MUST contain a `<SheetTitle>` so the accessible name is announced. Use `className="sr-only"` if the visible UI does not need a heading. Logical-property edges (`start-0` / `end-0`) ensure `left` and `right` automatically swap under `dir="rtl"`.
|
||||
|
||||
## Features
|
||||
|
||||
- Four side anchors (`top`, `right`, `bottom`, `left`) with matching slide-in/slide-out animations.
|
||||
- Focus-trap on open; restore-focus on close (Radix-managed).
|
||||
- Escape closes; click on the overlay backdrop closes the sheet.
|
||||
- Translated close-button `aria-label` via `react-ui.sheet.close` (Close / 关闭 / إغلاق / Cau / Закрыть).
|
||||
- Theme-driven cva overrides for every slot via `useThemedVariants("Sheet<Slot>")`.
|
||||
- All sub-components forward refs to their underlying Radix elements.
|
||||
- `motion-reduce:animate-none` collapses the slide animation under `prefers-reduced-motion: reduce`.
|
||||
- Logical-property anchoring (`start-0` / `end-0`) so `left` and `right` flip in RTL automatically.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Sheet,
|
||||
SheetClose,
|
||||
SheetContent,
|
||||
SheetDescription,
|
||||
SheetFooter,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
SheetTrigger,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function FiltersSheet() {
|
||||
return (
|
||||
<Sheet>
|
||||
<SheetTrigger>Filters</SheetTrigger>
|
||||
<SheetContent side="right">
|
||||
<SheetHeader>
|
||||
<SheetTitle>Filters</SheetTitle>
|
||||
<SheetDescription>Refine the results.</SheetDescription>
|
||||
</SheetHeader>
|
||||
<SheetFooter>
|
||||
<SheetClose>Apply</SheetClose>
|
||||
</SheetFooter>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Button,
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetDescription,
|
||||
SheetFooter,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
SheetTrigger,
|
||||
} from "@suaveplan/react-ui";
|
||||
import { useState } from "react";
|
||||
|
||||
interface NavItem {
|
||||
readonly id: string;
|
||||
readonly label: string;
|
||||
}
|
||||
|
||||
export function MobileNavSheet({ items }: { items: readonly NavItem[] }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<Sheet open={open} onOpenChange={setOpen}>
|
||||
<SheetTrigger asChild>
|
||||
<Button variant="outline">Open menu</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent side="left">
|
||||
<SheetHeader>
|
||||
<SheetTitle>Navigation</SheetTitle>
|
||||
<SheetDescription>Browse the app.</SheetDescription>
|
||||
</SheetHeader>
|
||||
<nav className="mt-4 flex flex-col gap-1">
|
||||
{items.map((item) => (
|
||||
<a
|
||||
key={item.id}
|
||||
href={`#${item.id}`}
|
||||
className="rounded px-3 py-2 hover:bg-accent"
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
))}
|
||||
</nav>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Sheet>`
|
||||
|
||||
Renders the Radix Dialog Root. Same lifecycle props as `<Dialog>`.
|
||||
|
||||
### `<SheetContent>`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `side` | `"top" \| "right" \| "bottom" \| "left"` | `"right"` | Anchor edge (RTL flips left/right). |
|
||||
| `closeAriaLabelKey` | `TranslationKey` | `react-ui.sheet.close` | Override the close-button accessible name. |
|
||||
|
||||
### `<SheetTitle>`
|
||||
|
||||
REQUIRED — Radix surfaces a dev warning when omitted. Render `className="sr-only"` if no visible heading is needed.
|
||||
|
||||
### `<SheetTrigger>`, `<SheetPortal>`, `<SheetOverlay>`, `<SheetHeader>`, `<SheetFooter>`, `<SheetDescription>`, `<SheetClose>`
|
||||
|
||||
Same as Dialog's analogous sub-components — direct re-exports of the Radix sub-components with the chassis cva applied.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The Radix Content props type is a discriminated union once cva-extra variant props (`side`) are layered on; TS cannot narrow the spread back through the chassis surface, so `<SheetContent>` casts the Radix Content to a relaxed shape via `unknown`. The runtime accepts both shapes equivalently. This mirrors the Wave 3b ToggleGroup reconciliation finding and is documented in the implementation comment.
|
||||
- The chassis NEVER assigns a numeric `z-index` to the overlay or content — Radix portals handle stacking.
|
||||
- `start-0` / `end-0` are logical properties — `left` / `right` automatically swap in RTL without a chevron-flip step.
|
||||
- `motion-reduce:animate-none motion-reduce:transition-none` collapses both the slide and the duration transitions under `prefers-reduced-motion: reduce`.
|
||||
- `<SheetContent>` self-portals via `<SheetPortal>` so consumers do not need to remember to portal manually — matches the shadcn upstream convention exactly.
|
||||
@@ -0,0 +1,66 @@
|
||||
# Sheet Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`sheetOverlayVariants`, `sheetContentVariants`, `sheetHeaderVariants`, `sheetFooterVariants`, `sheetTitleVariants`, `sheetDescriptionVariants`, and `sheetCloseButtonVariants` are the cva configurations powering every `<Sheet>` slot. They are split from `sheet.tsx` into their own module so an ejected source copy under `npx shadcn@latest add @suaveplan/sheet` keeps the cva configurations in a dedicated file. Theme overrides flow through `useThemedVariants("Sheet<Slot>")`.
|
||||
|
||||
The content cva is the only slot with a variant axis (`side`); all other slots are layout-only so consumers can compose freely.
|
||||
|
||||
## Features
|
||||
|
||||
- Overlay: `fixed inset-0 bg-black/80` with fade animations driven by Radix's `data-state`.
|
||||
- Content: four sides (`top`, `right`, `bottom`, `left`) with matching slide-in/slide-out animations and edge anchoring.
|
||||
- `start-0` / `end-0` (logical properties) on `left`/`right` so RTL flips automatically.
|
||||
- Header / footer / title / description: layout-only utilities matching the shadcn upstream `sheet` recipe.
|
||||
- Close button: pinned at `end-4 top-4` (logical), focus-ring + opacity hover.
|
||||
- `motion-reduce:animate-none motion-reduce:transition-none` on the content so the slide collapses under reduced-motion.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { sheetContentVariants, sheetTitleVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const contentClassName = sheetContentVariants({ side: "right" });
|
||||
const titleClassName = sheetTitleVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { sheetContentVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomSidePanel() {
|
||||
return (
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
className={cn(
|
||||
sheetContentVariants({ side: "left" }),
|
||||
"border-l-4 border-primary"
|
||||
)}
|
||||
>
|
||||
…
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `sheetOverlayVariants(): string`
|
||||
|
||||
Returns the overlay backdrop classes; takes no arguments.
|
||||
|
||||
### `sheetContentVariants(props?: { side?: "top" | "right" | "bottom" | "left" }): string`
|
||||
|
||||
Returns the panel classes. Omitted side falls back to `defaultVariants.side = "right"`.
|
||||
|
||||
### `sheetHeaderVariants(): string` / `sheetFooterVariants(): string` / `sheetTitleVariants(): string` / `sheetDescriptionVariants(): string` / `sheetCloseButtonVariants(): string`
|
||||
|
||||
Each returns a single class string. None take arguments.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The `left` and `right` side classes use logical properties (`start-0` / `end-0` and `border-s` / `border-e`) so the panel anchors correctly in both LTR and RTL — RTL automatically flips `start` ↔ `end` via the CSS spec.
|
||||
- The chassis NEVER assigns a numeric `z-index`. Radix's portal hosts the content above all page content via DOM placement.
|
||||
- The slide-in/out class names (`slide-in-from-top`, `slide-out-to-right`, …) require the consumer's Tailwind setup to load `tailwindcss-animate` — the chassis ships this via `@suaveplan/react-ui/styles/global.css`.
|
||||
100
docs/ui/react/react-ui/src/components/skeleton/skeleton.md
Normal file
100
docs/ui/react/react-ui/src/components/skeleton/skeleton.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# Skeleton
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - skeleton.variants — `skeletonVariants` is the cva (…
|
||||
- [skeleton.variants](./skeleton.variants.md) — `skeletonVariants` is the cva (class-variance-authority) configuration that powers every `<Skeleton>` render. It is spl…
|
||||
- skeleton.variants — `skeletonVariants` is the cva (…
|
||||
|
||||
- [skeleton.variants](./skeleton.variants.md) — `skeletonVariants` is the cva (class-variance-authority) configuration that powers every `<Skeleton>` render. It is spl…
|
||||
|
||||
- skeleton.variants — `skeletonVariants` is the cva (…
|
||||
|
||||
- [skeleton.variants](./skeleton.variants.md) — `skeletonVariants` is the cva (class-variance-authority) configuration that powers every `<Skeleton>` render. It is spl…
|
||||
|
||||
- skeleton.variants — `skeletonVariants` is the cva (…
|
||||
|
||||
- [skeleton.variants](./skeleton.variants.md) — `skeletonVariants` is the cva (class-variance-authority) configuration that powers every `<Skeleton>` render. It is spl…
|
||||
- [skeleton.variants](./skeleton.variants.md) — `skeletonVariants` is the cva (class-variance-authority) configuration that powers every `<Skeleton>` render. It is spl…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Skeleton>` is the chassis loading-placeholder primitive for `@suaveplan/react-ui`. It renders a muted-background `<div>` with a pulse animation that occupies the layout space of content the consumer has not yet finished loading. The pulse animation is gated behind `motion-safe:animate-pulse` so users with `prefers-reduced-motion: reduce` see a static placeholder block; everyone else gets the standard shadcn pulse.
|
||||
|
||||
The component is a pure visual placeholder — sizing, layout, and aspect-ratio control come from consumer-supplied `className` (`h-4 w-32`, `h-12 w-12 rounded-full` for an avatar placeholder, etc.). The chassis surface is intentionally minimal so consumers can compose any skeleton layout they need without fighting opinionated defaults.
|
||||
|
||||
## Features
|
||||
|
||||
- Single `default` cva variant; consumer-supplied `className` carries the sizing.
|
||||
- `motion-safe:animate-pulse` + `motion-reduce:animate-none` — animation is gated by `prefers-reduced-motion` so reduced-motion users see a static placeholder.
|
||||
- `data-skeleton="true"` attribute on the rendered element so tests / screen-readers / consumer CSS can target every skeleton placeholder via a single selector.
|
||||
- Forwards refs to the underlying `<div>`.
|
||||
- All native `HTMLAttributes` are forwarded as-is so consumers can drop `aria-hidden`, `role`, `id`, `aria-label`, etc.
|
||||
- Theme variant overrides via `useThemedVariants("Skeleton", skeletonVariants)`. A `ThemePlugin.variantOverrides.Skeleton` may add brand-new variants (e.g. `variant: "shimmer"` for a more elaborate animation).
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { Skeleton } from "@suaveplan/react-ui";
|
||||
|
||||
export function PostPreview() {
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<Skeleton className="h-4 w-3/4" />
|
||||
<Skeleton className="h-4 w-full" />
|
||||
<Skeleton className="h-4 w-1/2" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { Skeleton } from "@suaveplan/react-ui";
|
||||
|
||||
export function UserCardSkeleton() {
|
||||
return (
|
||||
<div className="flex items-center gap-3" role="status" aria-busy="true">
|
||||
<Skeleton
|
||||
className="h-12 w-12 rounded-full"
|
||||
aria-label="loading-avatar"
|
||||
/>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Skeleton className="h-4 w-32" />
|
||||
<Skeleton className="h-4 w-48" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Skeleton>`
|
||||
|
||||
`forwardRef<HTMLDivElement, SkeletonProps>` — renders a `<div>` with the variant classes + consumer-supplied `className` merged via `cn()`.
|
||||
|
||||
#### `SkeletonProps`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `variant` | `"default"` | `"default"` | Visual variant; the chassis ships only `default` so themes carry the variation. |
|
||||
| _all_ `HTMLAttributes<HTMLDivElement>` | _native_ | _native_ | Forwarded as-is (`className`, `style`, `id`, `aria-*`, `role`, etc.). |
|
||||
|
||||
#### `skeletonVariants`
|
||||
|
||||
The bare cva resolver. Exported so external consumers (`@suaveplan/react-ui-theme-*`, ejected source under `npx shadcn@latest add @suaveplan/skeleton`) can re-use the variants directly without going through `useThemedVariants`.
|
||||
|
||||
#### Errors
|
||||
|
||||
The Skeleton component does NOT call `useTranslation()` so it does not interact with the i18n provider at all. It is safe to render outside a `<TranslationProvider>` / `<ReactUiProvider>`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis hardcodes `motion-safe:animate-pulse` and `motion-reduce:animate-none` into the cva base rather than driving them off `motionTokens.respectReducedMotion` because Tailwind's `motion-safe` / `motion-reduce` variants honour `prefers-reduced-motion` natively at the CSS layer — no JS reactivity required.
|
||||
- `data-skeleton="true"` is always written so consumer CSS / tests can target every skeleton via `[data-skeleton="true"]`. The chassis does not omit it when other attributes change.
|
||||
- The variants module is intentionally separate (`skeleton.variants.ts`) so an ejected source copy keeps the cva config in a dedicated file, mirroring the upstream shadcn convention.
|
||||
- `useThemedVariants("Skeleton", skeletonVariants)` is called unconditionally so the hook order is stable across renders. The hook's memoisation key is `(themeName, "Skeleton")` so a re-render with the same theme returns the same resolver reference.
|
||||
- `bg-muted` is the cva-applied background colour; the actual colour value comes from the active theme's `cssVariables.muted` token.
|
||||
- The chassis does NOT inject `role="status"` automatically — consumer composition decides whether the surrounding container should be an ARIA live region. A user-card skeleton is typically wrapped in `<div role="status" aria-busy="true">` so AT announces the loading state once for the whole group, not once per skeleton primitive.
|
||||
@@ -0,0 +1,51 @@
|
||||
# Skeleton Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`skeletonVariants` is the cva (class-variance-authority) configuration that powers every `<Skeleton>` render. It is split from `skeleton.tsx` into its own module so an ejected source copy under `npx shadcn@latest add @suaveplan/skeleton` keeps the cva configuration in a dedicated file — exactly mirroring the upstream shadcn convention. Theme overrides flow through `useThemedVariants("Skeleton", skeletonVariants)`, so the runtime cva resolver can be replaced or extended by an active `ThemePlugin` without touching the component source.
|
||||
|
||||
The module exports a single `skeletonVariants` callable resolver (`(props?) => string`) tagged with the component name `"Skeleton"` via `defineVariants`. Consumers that want the bare resolver — for example to apply the skeleton styling to a `<span>` instead of a `<div>` — import `skeletonVariants` directly and apply the result via `cn()` themselves.
|
||||
|
||||
## Features
|
||||
|
||||
- Single `default` cva variant — sizing + aspect-ratio belong to consumer-supplied `className` so the chassis surface stays minimal.
|
||||
- `motion-safe:animate-pulse` so users WITHOUT `prefers-reduced-motion: reduce` see the standard shadcn pulse.
|
||||
- `motion-reduce:animate-none` so users WITH `prefers-reduced-motion: reduce` see a static placeholder block.
|
||||
- `rounded-md bg-muted` for the chassis baseline rounded muted-background look.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { skeletonVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const className = skeletonVariants({ variant: "default" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { skeletonVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function InlineSkeletonText({ length }: { length: number }) {
|
||||
return (
|
||||
<span
|
||||
data-skeleton="true"
|
||||
style={{ width: `${length}ch` }}
|
||||
className={cn(skeletonVariants({}), "inline-block h-4 align-middle")}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `skeletonVariants(props?: VariantProps<typeof skeletonVariants>): string`
|
||||
|
||||
Returns the cva-merged class string for the supplied variant. The `variant` field is optional; an omitted field falls back to the cva `defaultVariants.variant: "default"`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The resolver is tagged with `componentName: "Skeleton"` (non-enumerable, non-writable) via `defineVariants` so debug tooling can identify the cva instance at runtime.
|
||||
- The chassis hardcodes `motion-safe:animate-pulse` and `motion-reduce:animate-none` rather than driving them off `motionTokens.respectReducedMotion` because Tailwind's `motion-safe` / `motion-reduce` variants honour `prefers-reduced-motion` natively at the CSS layer — no JS reactivity required.
|
||||
- `bg-muted` references the theme-driven `--muted` CSS custom property so dark / high-contrast / dracula themes render an appropriate placeholder colour without per-theme overrides.
|
||||
- The cva config does NOT include sizing classes (`h-4`, `w-32`, etc.) — sizing is consumer-driven via `className` so consumers can render skeletons of any shape (line, avatar, card, paragraph) without per-shape variants.
|
||||
156
docs/ui/react/react-ui/src/components/sonner/sonner.md
Normal file
156
docs/ui/react/react-ui/src/components/sonner/sonner.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# `<Sonner>`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - sonner.variants — Defines the `sonnerToasterVariant…
|
||||
- [sonner.variants](./sonner.variants.md) — Defines the `sonnerToasterVariants` cva slot for the chassis `<Sonner>` toaster host. Sonner manages its own stacking,…
|
||||
- sonner.variants — Defines the `sonnerToasterVariant…
|
||||
|
||||
- [sonner.variants](./sonner.variants.md) — Defines the `sonnerToasterVariants` cva slot for the chassis `<Sonner>` toaster host. Sonner manages its own stacking,…
|
||||
|
||||
- sonner.variants — Defines the `sonnerToasterVariant…
|
||||
|
||||
- [sonner.variants](./sonner.variants.md) — Defines the `sonnerToasterVariants` cva slot for the chassis `<Sonner>` toaster host. Sonner manages its own stacking,…
|
||||
|
||||
- sonner.variants — Defines the `sonnerToasterVariant…
|
||||
|
||||
- [sonner.variants](./sonner.variants.md) — Defines the `sonnerToasterVariants` cva slot for the chassis `<Sonner>` toaster host. Sonner manages its own stacking,…
|
||||
- [sonner.variants](./sonner.variants.md) — Defines the `sonnerToasterVariants` cva slot for the chassis `<Sonner>` toaster host. Sonner manages its own stacking,…
|
||||
|
||||
## Purpose
|
||||
|
||||
The chassis Toaster host built on `sonner` (the canonical shadcn toast
|
||||
surface). Mounted ONCE at the app root so every component in the tree
|
||||
can call the re-exported `toast()` function to emit notifications
|
||||
without prop-drilling a queue. Per the shadcn skill rule, the chassis
|
||||
does not invent a toast wrapper component — `sonner` already covers
|
||||
every common case (success, error, info, warning, loading, promise,
|
||||
action, dismiss). The chassis adds a thin observability shim so every
|
||||
emit increments `react_ui.toast.shown_total{toast.kind=...}` without
|
||||
changing the consumer-facing API.
|
||||
|
||||
## Features
|
||||
|
||||
- Re-exports `sonner.toast` (callable + `success` / `error` / `info`
|
||||
/ `warning` / `loading` / `promise` / `dismiss` / `message` /
|
||||
`custom` / `getHistory` / `getToasts`) with OTel instrumentation
|
||||
attached to each kind.
|
||||
- Maps the active `<ThemeProvider>` theme onto sonner's `theme` prop
|
||||
automatically so toasts pick up the active palette without
|
||||
consumer-side wiring.
|
||||
- Per Wave 3c finding #3, NO numeric `z-index` (including `z-50`) is
|
||||
applied — sonner manages stacking via its own CSS variable.
|
||||
- Cva slot `Sonner` registered into `ThemableVariants` so brand themes
|
||||
can layer utilities (safe-area padding, font overrides) without
|
||||
forking the host.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { ReactUiProvider, Sonner, toast } from "@suaveplan/react-ui";
|
||||
|
||||
function App(): JSX.Element {
|
||||
return (
|
||||
<ReactUiProvider {...providerProps}>
|
||||
<button type="button" onClick={() => toast.success("Saved")}>
|
||||
Save
|
||||
</button>
|
||||
<Sonner />
|
||||
</ReactUiProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { Sonner, toast } from "@suaveplan/react-ui";
|
||||
|
||||
function CheckoutPage(): JSX.Element {
|
||||
const onSubmit = async () => {
|
||||
const id = toast.loading("Processing payment");
|
||||
try {
|
||||
await checkout();
|
||||
toast.success("Payment received", { id });
|
||||
} catch (err) {
|
||||
toast.error("Payment failed", { id, description: String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<button type="button" onClick={onSubmit}>
|
||||
Pay
|
||||
</button>
|
||||
<Sonner
|
||||
position="bottom-right"
|
||||
richColors
|
||||
expand
|
||||
visibleToasts={5}
|
||||
duration={6000}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Sonner>` props
|
||||
|
||||
Mirrors `ToasterProps` from `sonner` 1.7.x (`position`, `theme`,
|
||||
`richColors`, `closeButton`, `expand`, `gap`, `offset`, `dir`,
|
||||
`visibleToasts`, `duration`, `pauseWhenPageIsHidden`, `loadingIcon`,
|
||||
…). Extends with the cva `className` slot.
|
||||
|
||||
When `theme` is omitted, the chassis maps the active
|
||||
`<ThemeProvider>` theme name (`light` / `dark`) onto sonner's
|
||||
matching mode and falls back to `"system"` for any other theme name.
|
||||
|
||||
### `toast(message, options?) => string | number`
|
||||
|
||||
Re-export of `sonner.toast`. Records
|
||||
`react_ui.toast.shown_total{toast.kind="default"}` then forwards.
|
||||
|
||||
#### `toast.success(message, options?)`
|
||||
|
||||
Records `toast.kind="success"` then calls `sonner.toast.success`.
|
||||
|
||||
#### `toast.error(message, options?)`
|
||||
|
||||
Records `toast.kind="error"` then calls `sonner.toast.error`.
|
||||
|
||||
#### `toast.info(message, options?)`
|
||||
|
||||
Records `toast.kind="info"` then calls `sonner.toast.info`.
|
||||
|
||||
#### `toast.warning(message, options?)`
|
||||
|
||||
Records `toast.kind="warning"` then calls `sonner.toast.warning`.
|
||||
|
||||
#### `toast.loading` / `toast.promise` / `toast.message` / `toast.custom` / `toast.dismiss` / `toast.getHistory` / `toast.getToasts`
|
||||
|
||||
Forwarded as-is — these emit non-counter events (loading is a
|
||||
transient state, promise resolves into success/error which are
|
||||
already counted).
|
||||
|
||||
### `sonnerToasterVariants(): string`
|
||||
|
||||
Empty-base cva resolver registered under the `Sonner` name in
|
||||
`ThemableVariants`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The `toast` re-export is a `Object.assign(callable, namespace)`
|
||||
composition so it satisfies sonner's intersection-typed `toast`
|
||||
signature (callable AND record of overload methods).
|
||||
- The OTel side-effect uses `recordToastShown` from the chassis
|
||||
`react-ui-metrics` module which populates the in-memory state ring
|
||||
spec-conformance tests assert against.
|
||||
- Theme mapping deliberately falls back to `"system"` for unknown
|
||||
theme names rather than `"dark"` because most brand themes derive
|
||||
from the system surface and benefit from the user's OS preference
|
||||
override.
|
||||
- The `Sonner` host invokes `useTheme()` so it MUST live inside a
|
||||
`<ThemeProvider>`. The `<ReactUiProvider>` already guarantees this
|
||||
ordering.
|
||||
@@ -0,0 +1,63 @@
|
||||
# `sonner.variants`
|
||||
|
||||
## Purpose
|
||||
|
||||
Defines the `sonnerToasterVariants` cva slot for the chassis `<Sonner>`
|
||||
toaster host. Sonner manages its own stacking, animation, and visual
|
||||
treatment via CSS variables (`--toaster-*`); the chassis exposes a
|
||||
single empty cva slot so brand themes can inject incidental utility
|
||||
classes (safe-area padding, font-stack overrides) without forking the
|
||||
underlying library.
|
||||
|
||||
## Features
|
||||
|
||||
- Single registered cva: `sonnerToasterVariants` keyed `"Sonner"` so
|
||||
`ThemePlugin.variantOverrides.Sonner` can layer brand utilities.
|
||||
- Zero default classes — sonner paints itself; the chassis adds
|
||||
nothing by default.
|
||||
- Per Wave 3c finding #3, no `z-index` is layered on the host. Sonner
|
||||
uses `position: fixed` with its own `--toaster-z-index` CSS variable.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { sonnerToasterVariants } from "@suaveplan/react-ui/components/sonner/sonner";
|
||||
|
||||
const className = sonnerToasterVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { defineVariants } from "@suaveplan/react-ui/variants";
|
||||
import type { ThemePlugin } from "@suaveplan/react-ui";
|
||||
|
||||
const brandTheme: ThemePlugin = {
|
||||
name: "brand",
|
||||
tokens: { /* ... */ },
|
||||
variantOverrides: {
|
||||
Sonner: defineVariants("Sonner", "pt-safe-top px-2", {
|
||||
variants: {},
|
||||
defaultVariants: {},
|
||||
}),
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `sonnerToasterVariants(): string` — resolves to the empty base class
|
||||
by default. Returns a stable identity per call.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The empty base intentionally communicates "no chassis-side styling"
|
||||
to consumers reading the source. Any future visual treatment (e.g.
|
||||
`role="region"`-style backdrops, custom safe-area padding for mobile
|
||||
notch devices, or font-stack overrides for non-Latin glyph coverage)
|
||||
belongs in the brand theme override or in a sibling cva slot, not on
|
||||
this base — sonner's painting must remain authoritative so updates
|
||||
to the upstream library do not silently regress the chassis. The
|
||||
override path is exercised by the chassis e2e suite, which mounts a
|
||||
brand theme and asserts that the `<ol data-sonner-toaster>` host
|
||||
carries the merged className.
|
||||
87
docs/ui/react/react-ui/src/components/spinner/spinner.md
Normal file
87
docs/ui/react/react-ui/src/components/spinner/spinner.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# Spinner
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - spinner.variants — `spinnerVariants` is the cva con…
|
||||
- [spinner.variants](./spinner.variants.md) — `spinnerVariants` is the cva config that powers the `<Spinner>` chassis component. It exposes a `size` axis (`sm` / `de…
|
||||
- spinner.variants — `spinnerVariants` is the cva con…
|
||||
|
||||
- [spinner.variants](./spinner.variants.md) — `spinnerVariants` is the cva config that powers the `<Spinner>` chassis component. It exposes a `size` axis (`sm` / `de…
|
||||
|
||||
- spinner.variants — `spinnerVariants` is the cva con…
|
||||
|
||||
- [spinner.variants](./spinner.variants.md) — `spinnerVariants` is the cva config that powers the `<Spinner>` chassis component. It exposes a `size` axis (`sm` / `de…
|
||||
|
||||
- spinner.variants — `spinnerVariants` is the cva con…
|
||||
|
||||
- [spinner.variants](./spinner.variants.md) — `spinnerVariants` is the cva config that powers the `<Spinner>` chassis component. It exposes a `size` axis (`sm` / `de…
|
||||
- [spinner.variants](./spinner.variants.md) — `spinnerVariants` is the cva config that powers the `<Spinner>` chassis component. It exposes a `size` axis (`sm` / `de…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Spinner>` is the chassis circular loading indicator for `@suaveplan/react-ui`. It renders a `<div>` with one transparent border edge that rotates via Tailwind's `animate-spin`, creating the familiar "spinning arc" pattern. The animation is gated behind `motion-safe:animate-spin` so users with `prefers-reduced-motion: reduce` see a static circle while everyone else sees the rotation.
|
||||
|
||||
The component mirrors the upstream shadcn `spinner` registry item (per Wave 3a's reconciliation finding #6: shadcn ships `spinner` as a discrete registry item and the chassis mirrors that surface). Per the shadcn skill rule, the recommended composition for a button loading state is `<Spinner>` + `data-icon` + `disabled` — the `Button` itself does NOT carry an `isLoading` prop because hardcoding the loading affordance into the Button forecloses every other composition consumers might want (label-only spinner, spinner-with-progress, replace-trigger spinner, etc.).
|
||||
|
||||
## Features
|
||||
|
||||
- Three sizes via the `size` axis (`sm` / `default` / `lg`) using Tailwind `size-*`.
|
||||
- `motion-safe:animate-spin` + `motion-reduce:animate-none` so reduced-motion users see a static circle.
|
||||
- `role="status"` automatically applied so screen-readers announce the loading state.
|
||||
- `data-spinner="true"` attribute on the rendered element so tests / consumer CSS can target every spinner via a single selector.
|
||||
- Border colour inherits via `border-current` — consumers recolour with any `text-*` utility.
|
||||
- Forwards refs to the underlying `<div>`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { Spinner } from "@suaveplan/react-ui";
|
||||
|
||||
export function LoadingIndicator() {
|
||||
return <Spinner />;
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { Button, Spinner } from "@suaveplan/react-ui";
|
||||
|
||||
export function SaveButton({ isSaving }: { isSaving: boolean }) {
|
||||
return (
|
||||
<Button disabled={isSaving}>
|
||||
{isSaving ? <Spinner data-icon="inline-start" size="sm" /> : null}
|
||||
{isSaving ? "Saving…" : "Save changes"}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Spinner>`
|
||||
|
||||
`forwardRef<HTMLDivElement, SpinnerProps>` — renders a `<div>` with the variant classes + consumer-supplied `className` merged via `cn()`. `role="status"` and `data-spinner="true"` are always applied.
|
||||
|
||||
#### `SpinnerProps`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Diameter — uses Tailwind `size-*` to set width and height together. |
|
||||
| _all_ `HTMLAttributes<HTMLDivElement>` | _native_ | _native_ | Forwarded as-is — `aria-label`, `data-icon`, `id`, `style`, etc. |
|
||||
|
||||
#### `spinnerVariants`
|
||||
|
||||
The bare cva resolver — exported so external consumers (`@suaveplan/react-ui-theme-*`, ejected source) can re-use the variants directly.
|
||||
|
||||
#### Errors
|
||||
|
||||
The Spinner component does NOT call `useTranslation()` so it does not interact with the i18n provider at all. It is safe to render outside a `<TranslationProvider>` / `<ReactUiProvider>`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `motion-safe:animate-spin` and `motion-reduce:animate-none` are baked into the cva base class string rather than driven from `motionTokens.respectReducedMotion` — Tailwind's `motion-*` variants honour `prefers-reduced-motion` natively at the CSS layer, so no JS reactivity is required and the spinner stays a leaf component.
|
||||
- `role="status"` is unconditionally applied so screen-readers announce "loading" without requiring the consumer to remember to wrap in a live region. When the spinner sits inside a Button that already has its own accessible name (e.g. "Save changes"), the consumer can suppress the duplicate announcement by passing `aria-hidden="true"`.
|
||||
- The chassis intentionally does NOT inject default `aria-label="Loading"` text — that would force every consumer to pay the i18n cost for a spinner that is often purely decorative. Consumers that need a translated label compose with `t('react-ui.progress.fallback')` or any other `TranslationKey`.
|
||||
- The `data-spinner="true"` selector is preserved across all variants; `[data-spinner="true"]` works as a global CSS hook.
|
||||
- Border thickness scales with size (`border-2` for `sm`/`default`, `border-[3px]` for `lg`) so the spinner's arc-to-diameter ratio remains visually consistent at every size.
|
||||
@@ -0,0 +1,50 @@
|
||||
# Spinner Variants
|
||||
|
||||
## Purpose
|
||||
|
||||
`spinnerVariants` is the cva config that powers the `<Spinner>` chassis component. It exposes a `size` axis (`sm` / `default` / `lg`) using Tailwind's `size-*` shorthand so the rendered diameter changes in lockstep with width and height. The base classes establish the circular shape, the transparent top-edge that creates the "spinning arc" illusion, and the `motion-safe:animate-spin` gating that disables the rotation for users with `prefers-reduced-motion: reduce`.
|
||||
|
||||
The variants module sits in its own file so an ejected source copy under `npx shadcn@latest add @suaveplan/spinner` keeps the cva config separate from the component logic, matching the upstream shadcn convention and easing per-project customisation without touching the JSX.
|
||||
|
||||
## Features
|
||||
|
||||
- Single `size` axis with three rungs (`sm` 16px, `default` 20px, `lg` 32px) using Tailwind `size-*`.
|
||||
- Border thickness scales with size — 2px for `sm`/`default`, 3px for `lg` — keeping the arc visually proportional.
|
||||
- `motion-safe:animate-spin` + `motion-reduce:animate-none` so reduced-motion users see a static circle.
|
||||
- Border colour inherits via `border-current` so consumers can recolour the spinner with a single `text-*` utility.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { spinnerVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const small = spinnerVariants({ size: "sm" });
|
||||
const large = spinnerVariants({ size: "lg" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { cva } from "class-variance-authority";
|
||||
import { spinnerVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const customSpinner = cva(
|
||||
[spinnerVariants({ size: "default" }), "text-emerald-500"].join(" ")
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `spinnerVariants(opts?)`
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Diameter — uses Tailwind `size-*` to set width and height together. |
|
||||
|
||||
Returns the merged class string.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The `border-t-transparent` rule is what creates the "missing arc" illusion that reads as motion when the element rotates. Without it the spinner would be a solid ring with no visual rotation.
|
||||
- `text-foreground/60` is applied at the cva level so the spinner renders with a subtle muted colour by default. Consumers override with any `text-*` utility (`text-primary`, `text-emerald-500`, etc.).
|
||||
- The spinner deliberately does not register `display`-related classes — `inline-block` is hard-coded so the spinner sits inline with adjacent text inside a Button without being affected by the Button's `flex` context.
|
||||
@@ -0,0 +1,97 @@
|
||||
# StatusBadge
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - status-badge.variants — `statusBadgeVariants` is th…
|
||||
- [status-badge.variants](./status-badge.variants.md) — `statusBadgeVariants` is the cva config that powers the `<StatusBadge>` chassis component. It exposes a `status` axis w…
|
||||
- status-badge.variants — `statusBadgeVariants` is th…
|
||||
|
||||
- [status-badge.variants](./status-badge.variants.md) — `statusBadgeVariants` is the cva config that powers the `<StatusBadge>` chassis component. It exposes a `status` axis w…
|
||||
|
||||
- status-badge.variants — `statusBadgeVariants` is th…
|
||||
|
||||
- [status-badge.variants](./status-badge.variants.md) — `statusBadgeVariants` is the cva config that powers the `<StatusBadge>` chassis component. It exposes a `status` axis w…
|
||||
|
||||
- status-badge.variants — `statusBadgeVariants` is th…
|
||||
|
||||
- [status-badge.variants](./status-badge.variants.md) — `statusBadgeVariants` is the cva config that powers the `<StatusBadge>` chassis component. It exposes a `status` axis w…
|
||||
- [status-badge.variants](./status-badge.variants.md) — `statusBadgeVariants` is the cva config that powers the `<StatusBadge>` chassis component. It exposes a `status` axis w…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<StatusBadge>` is the chassis status marker with semantic colour and matching iconography. It pairs one of five status colour palettes (`success` / `warning` / `error` / `info` / `neutral`) with a default lucide-react icon (`CheckCircle2Icon` / `AlertTriangleIcon` / `XCircleIcon` / `InfoIcon` / `CircleIcon`) so consumers do not hand-roll status-with-icon markers across the application surface.
|
||||
|
||||
`<StatusBadge>` is a **suaveplan-only addition** — shadcn does not ship a `status-badge` registry item upstream (per Wave 3a's reconciliation finding #7). The chassis exposes it because every product surface needs status indicators, and hand-rolled spans diverge across teams (different padding, different colours, different iconography). A discrete primitive enforces consistency.
|
||||
|
||||
## Features
|
||||
|
||||
- Five status colour palettes via the `status` axis (`success` / `warning` / `error` / `info` / `neutral`).
|
||||
- Default lucide-react icon per status — overridable via `hideIcon` and a custom child element.
|
||||
- Translucent backgrounds with matching borders that meet WCAG AA contrast in both light and dark themes.
|
||||
- `data-status` attribute on the rendered element so tests / consumer CSS can target every status badge by status family.
|
||||
- `aria-hidden="true"` on the injected icon so the visible text (children) is the accessible name.
|
||||
- Forwards refs to the underlying `<div>`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { StatusBadge } from "@suaveplan/react-ui";
|
||||
|
||||
export function StatusRow() {
|
||||
return (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<StatusBadge status="success">Active</StatusBadge>
|
||||
<StatusBadge status="warning">Pending</StatusBadge>
|
||||
<StatusBadge status="error">Failed</StatusBadge>
|
||||
<StatusBadge status="info">In review</StatusBadge>
|
||||
<StatusBadge status="neutral">Idle</StatusBadge>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { StatusBadge } from "@suaveplan/react-ui";
|
||||
import { ZapIcon } from "lucide-react";
|
||||
|
||||
export function CustomIconStatus() {
|
||||
return (
|
||||
<StatusBadge status="success" hideIcon>
|
||||
<ZapIcon className="size-3" aria-hidden="true" />
|
||||
Lightning fast
|
||||
</StatusBadge>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<StatusBadge>`
|
||||
|
||||
`forwardRef<HTMLDivElement, StatusBadgeProps>` — renders a `<div>` with the variant classes + chassis-injected icon (unless `hideIcon === true`) + children.
|
||||
|
||||
#### `StatusBadgeProps`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `status` | `"success" \| "warning" \| "error" \| "info" \| "neutral"` | `"neutral"` | Semantic colour palette + default icon. |
|
||||
| `hideIcon` | `boolean` | `false` | When `true`, suppress the chassis-injected icon. Use when children includes a custom icon to avoid double-up. |
|
||||
| _all_ `HTMLAttributes<HTMLDivElement>` | _native_ | _native_ | Forwarded as-is. |
|
||||
|
||||
#### `statusBadgeVariants`
|
||||
|
||||
The bare cva resolver — exported so external consumers (themes, ejected source) can re-use the variants directly.
|
||||
|
||||
#### Errors
|
||||
|
||||
The StatusBadge component does NOT call `useTranslation()` so it does not interact with the i18n provider. Children are consumer-provided `ReactNode`; consumers using TranslationKey-typed strings should resolve them at the call site (`<StatusBadge>{t('status.active')}</StatusBadge>`).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The default icon for each status is fixed via the `STATUS_ICON` map. To replace the icon, pass `hideIcon` and provide a custom icon element as the first child — this keeps the chassis API simple while leaving the override path open.
|
||||
- `aria-hidden="true"` is applied to the injected icon so the icon does not duplicate the announcement of the status text. When `hideIcon === true` and the consumer provides their own icon, they are responsible for marking it `aria-hidden` themselves.
|
||||
- The `data-status` attribute is always written on the rendered `<div>` regardless of whether the icon is shown — this is the canonical hook for tests / consumer CSS targeting status badges by family.
|
||||
- The icon size is `size-3` (12px) which matches the badge's `text-xs` typography rhythm; consumers wanting a larger icon should pass `hideIcon` and supply their own sized icon.
|
||||
- RTL: the icon sits at the start (logical inline-start) so `dir="rtl"` automatically flips it to the right. No special-case markup needed.
|
||||
@@ -0,0 +1,55 @@
|
||||
# StatusBadge Variants
|
||||
|
||||
## Purpose
|
||||
|
||||
`statusBadgeVariants` is the cva config that powers the `<StatusBadge>` chassis component. It exposes a `status` axis with five rungs (`success` / `warning` / `error` / `info` / `neutral`) that each pair a translucent background, matching border colour, and a foreground tint that meets WCAG AA against the surface background. The variants module is intentionally separate so an ejected source copy keeps the cva config in a dedicated file.
|
||||
|
||||
`StatusBadge` is a **suaveplan-only addition** — shadcn does not ship a `status-badge` registry item upstream (per Wave 3a's reconciliation finding #7).
|
||||
|
||||
## Features
|
||||
|
||||
- Single `status` axis with five rungs (`success` / `warning` / `error` / `info` / `neutral`).
|
||||
- Translucent backgrounds (`bg-{colour}-500/10`) with matching borders (`border-{colour}-500/30`) so the badge reads as a marker, not a button.
|
||||
- Dark-mode foreground compensation via `dark:text-{colour}-300`.
|
||||
- `transition-colors` + `motion-reduce:transition-none` so reduced-motion users see instant colour changes.
|
||||
- `gap-1` baked in so the chassis-injected icon spaces correctly from the label.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { statusBadgeVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const success = statusBadgeVariants({ status: "success" });
|
||||
const warning = statusBadgeVariants({ status: "warning" });
|
||||
const error = statusBadgeVariants({ status: "error" });
|
||||
const info = statusBadgeVariants({ status: "info" });
|
||||
const neutral = statusBadgeVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { cva } from "class-variance-authority";
|
||||
import { statusBadgeVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const blockquoteStatus = cva(
|
||||
[statusBadgeVariants({ status: "info" }), "italic"].join(" ")
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `statusBadgeVariants(opts?)`
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `status` | `"success" \| "warning" \| "error" \| "info" \| "neutral"` | `"neutral"` | Semantic colour pair driving background, border, and foreground. |
|
||||
|
||||
Returns the merged class string.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The default `status` is `neutral` so a `<StatusBadge>` without an explicit status renders as a muted marker (matching the conservative-default convention used elsewhere in the chassis).
|
||||
- Translucent backgrounds are `/10` opacity so the badge tints rather than dominates the surrounding surface; `border-{colour}/30` provides a distinct edge without competing visually.
|
||||
- Dark-mode foreground tints use `text-{colour}-300` rather than `text-{colour}-700` because the `bg-{colour}-500/10` overlay reads darker on the dark theme's elevated surface.
|
||||
- The icon spacing is handled at this level via `gap-1` so the chassis component does not need a separate flex wrapper.
|
||||
124
docs/ui/react/react-ui/src/components/switch/switch.md
Normal file
124
docs/ui/react/react-ui/src/components/switch/switch.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# Switch
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - switch.variants — `switchVariants` and `switchThumb…
|
||||
- [switch.variants](./switch.variants.md) — `switchVariants` and `switchThumbVariants` are the two cva (class-variance-authority) configurations that together powe…
|
||||
- switch.variants — `switchVariants` and `switchThumb…
|
||||
|
||||
- [switch.variants](./switch.variants.md) — `switchVariants` and `switchThumbVariants` are the two cva (class-variance-authority) configurations that together powe…
|
||||
|
||||
- switch.variants — `switchVariants` and `switchThumb…
|
||||
|
||||
- [switch.variants](./switch.variants.md) — `switchVariants` and `switchThumbVariants` are the two cva (class-variance-authority) configurations that together powe…
|
||||
|
||||
- switch.variants — `switchVariants` and `switchThumb…
|
||||
|
||||
- [switch.variants](./switch.variants.md) — `switchVariants` and `switchThumbVariants` are the two cva (class-variance-authority) configurations that together powe…
|
||||
- [switch.variants](./switch.variants.md) — `switchVariants` and `switchThumbVariants` are the two cva (class-variance-authority) configurations that together powe…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Switch>` is the chassis on/off toggle primitive built on `@radix-ui/react-switch`. Radix supplies the a11y semantics (`role="switch"`, `aria-checked` reflecting boolean state, native `Space` activation, `disabled` propagation); the chassis layers theme-driven variant resolution, the sliding-thumb render, and the cva `size` axis that scales both the track and the thumb proportionally.
|
||||
|
||||
The thumb cva is exported alongside the track cva so consumers can theme each independently. Refs are forwarded to the Radix Root which renders a styled `<button>`.
|
||||
|
||||
Icon-only switches (no visible label sibling) MUST supply an `aria-label` so screen readers can announce what is being toggled. The chassis does NOT inject a fallback because the label is domain-specific — only the consumer knows what is being switched on or off.
|
||||
|
||||
## Features
|
||||
|
||||
- Three sizes (`sm`, `default`, `lg`) scaling track + thumb proportionally.
|
||||
- Sliding thumb with `data-[state=checked]:translate-x-*` driving the on/off motion via Tailwind transforms.
|
||||
- Controlled and uncontrolled use via Radix `checked` / `defaultChecked` / `onCheckedChange`.
|
||||
- `disabled` blocks pointer + keyboard input and decays opacity.
|
||||
- `data-state="checked|unchecked"` mirrored on the rendered button.
|
||||
- Refs forwarded to the Radix Root.
|
||||
- `motion-reduce:transition-none` collapses both colour + transform transitions for `prefers-reduced-motion: reduce`.
|
||||
- Theme-driven variant overrides via `useThemedVariants("Switch", switchVariants)`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { Switch } from "@suaveplan/react-ui";
|
||||
|
||||
export function AirplaneToggle() {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="airplane" />
|
||||
<label htmlFor="airplane">Airplane mode</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { Switch } from "@suaveplan/react-ui";
|
||||
import { useState } from "react";
|
||||
|
||||
export function NotificationSettings() {
|
||||
const [enabled, setEnabled] = useState(true);
|
||||
const [marketing, setMarketing] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3 p-4 border rounded">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="notif">Notifications</label>
|
||||
<Switch
|
||||
id="notif"
|
||||
checked={enabled}
|
||||
onCheckedChange={setEnabled}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="marketing">Marketing emails</label>
|
||||
<Switch
|
||||
id="marketing"
|
||||
checked={marketing}
|
||||
disabled={!enabled}
|
||||
onCheckedChange={setMarketing}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Switch>`
|
||||
|
||||
`forwardRef<ComponentRef<typeof SwitchPrimitive.Root>, SwitchProps>` — wraps Radix Root, renders a `<button role="switch">` containing the sliding thumb.
|
||||
|
||||
#### `SwitchProps`
|
||||
|
||||
Inherits every prop from Radix `SwitchPrimitive.Root` except `asChild`. Adds:
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Visual variant scaling track + thumb. |
|
||||
|
||||
Common Radix props consumers will use:
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `checked` | `boolean` | _uncontrolled_ | Controlled state. |
|
||||
| `defaultChecked` | `boolean` | `false` | Initial state for uncontrolled use. |
|
||||
| `onCheckedChange` | `(checked: boolean) => void` | _none_ | Fires on state change. |
|
||||
| `disabled` | `boolean` | `false` | Blocks input + decays opacity. |
|
||||
| `required` | `boolean` | `false` | Native form-field `required`. |
|
||||
| `name` | `string` | _none_ | Native form-field name. |
|
||||
| `value` | `string` | `"on"` | Native form-field value when checked. |
|
||||
|
||||
#### `switchVariants` / `switchThumbVariants`
|
||||
|
||||
Both bare cva resolvers are exported so external consumers can re-use the variants directly.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The track and thumb cva configs are kept separate so consumer themes can override either independently — for example a theme that makes the thumb glossy without changing the track shape.
|
||||
- The thumb's `translate-x-*` per-size value is hard-coded into `switchThumbVariants` rather than computed because cva is a string-templating library, not a runtime calculator. The math is `track-width − thumb-width − padding-x*2`.
|
||||
- `motion-reduce:transition-none` applies to both the track (colour transition) and the thumb (transform transition) so reduced-motion users see instant state changes.
|
||||
- Refs target the Radix Root (the `<button>` element) — consumers can `.focus()` the switch programmatically through the forwarded ref.
|
||||
- The chassis does NOT auto-inject an `aria-label` for icon-only use — see `Purpose`.
|
||||
@@ -0,0 +1,66 @@
|
||||
# Switch Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`switchVariants` and `switchThumbVariants` are the two cva (class-variance-authority) configurations that together power every `<Switch>` render. They are split from `switch.tsx` into their own module so an ejected source copy under `npx shadcn@latest add @suaveplan/switch` keeps the cva configurations in a dedicated file. Theme overrides flow through `useThemedVariants("Switch", switchVariants)`, so the runtime cva resolver can be replaced or extended by an active `ThemePlugin` without touching the component source.
|
||||
|
||||
The track and thumb are deliberately kept as separate cva instances so a `ThemePlugin` can override one independently of the other — for example a glossy-thumb theme with a flat track.
|
||||
|
||||
## Features
|
||||
|
||||
- Three sizes (`sm` → `h-4 w-7`, `default` → `h-5 w-9`, `lg` → `h-6 w-11`) for both track and thumb.
|
||||
- `data-[state=checked]:bg-primary` flips the track colour when on.
|
||||
- `data-[state=checked]:translate-x-*` slides the thumb across the track.
|
||||
- `disabled:cursor-not-allowed disabled:opacity-50` for the native disabled state.
|
||||
- `motion-reduce:transition-none` collapses both colour + transform transitions for `prefers-reduced-motion: reduce`.
|
||||
- `focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2` for keyboard-only focus styling.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { switchVariants, switchThumbVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const trackClassName = switchVariants({ size: "default" });
|
||||
const thumbClassName = switchThumbVariants({ size: "default" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { switchVariants, switchThumbVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomSwitch() {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
role="switch"
|
||||
aria-checked="false"
|
||||
data-state="unchecked"
|
||||
className={cn(switchVariants({ size: "lg" }), "ring-2 ring-accent")}
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
switchThumbVariants({ size: "lg" }),
|
||||
"bg-accent-foreground"
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `switchVariants(props?: { size?: "sm" | "default" | "lg" }): string`
|
||||
|
||||
Returns the cva-merged class string for the track. Omitted props fall back to the cva `defaultVariants` (`size: "default"`).
|
||||
|
||||
### `switchThumbVariants(props?: { size?: "sm" | "default" | "lg" }): string`
|
||||
|
||||
Returns the cva-merged class string for the thumb. The size axis MUST match the track's size — mismatched sizes produce a thumb that overflows or under-fills the track.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Both resolvers are tagged with `componentName: "Switch"` / `componentName: "SwitchThumb"` (non-enumerable, non-writable) via `defineVariants`.
|
||||
- The thumb's `translate-x-*` per-size value is hard-coded rather than computed — cva is a string-templating library, not a runtime calculator. The math is `track-width − thumb-width − padding-x*2`.
|
||||
- The track and thumb size axes are kept symmetric so a single `size` prop on `<Switch>` propagates to both. A future change could make them independent if the chassis ever needs an `extra-large-thumb` shape.
|
||||
171
docs/ui/react/react-ui/src/components/table/table.md
Normal file
171
docs/ui/react/react-ui/src/components/table/table.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# Table
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - table.variants — `tableVariants` and its eight sub-…
|
||||
- [table.variants](./table.variants.md) — `tableVariants` and its eight sub-component cva resolvers (`tableHeaderVariants`, `tableBodyVariants`, `tableFooterVari…
|
||||
- table.variants — `tableVariants` and its eight sub-…
|
||||
|
||||
- [table.variants](./table.variants.md) — `tableVariants` and its eight sub-component cva resolvers (`tableHeaderVariants`, `tableBodyVariants`, `tableFooterVari…
|
||||
|
||||
- table.variants — `tableVariants` and its eight sub-…
|
||||
|
||||
- [table.variants](./table.variants.md) — `tableVariants` and its eight sub-component cva resolvers (`tableHeaderVariants`, `tableBodyVariants`, `tableFooterVari…
|
||||
|
||||
- table.variants — `tableVariants` and its eight sub-…
|
||||
|
||||
- [table.variants](./table.variants.md) — `tableVariants` and its eight sub-component cva resolvers (`tableHeaderVariants`, `tableBodyVariants`, `tableFooterVari…
|
||||
- [table.variants](./table.variants.md) — `tableVariants` and its eight sub-component cva resolvers (`tableHeaderVariants`, `tableBodyVariants`, `tableFooterVari…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Table>` is the chassis pure-HTML table family — `<Table>` (root, wrapping `<table>`), `<TableHeader>` (`<thead>`), `<TableBody>` (`<tbody>`), `<TableFooter>` (`<tfoot>`), `<TableRow>` (`<tr>`), `<TableHead>` (`<th>`), `<TableCell>` (`<td>`), `<TableCaption>` (`<caption>`). The chassis exposes NO variants — typography and spacing are baked in so every chassis table renders consistently across consumers. The root wraps the native `<table>` in an `overflow-auto` container so wide tables scroll horizontally without breaking the layout.
|
||||
|
||||
For a headless data-table with sorting, pagination, and selection on top of these primitives, use `<DataTable>`.
|
||||
|
||||
## Features
|
||||
|
||||
- Eight composition primitives mapping 1:1 to native HTML table elements.
|
||||
- `overflow-auto` wrapper on the root so wide tables scroll horizontally.
|
||||
- Logical-property text alignment (`text-start` on `<TableHead>`) so RTL renders correctly.
|
||||
- Hover + selected (`data-state="selected"`) row states on `<TableRow>`.
|
||||
- Footer reads as a summary surface (`bg-muted/50 font-medium`).
|
||||
- Caption sits below the table (`caption-bottom`) per conventional reading order.
|
||||
- All sub-components forward refs to their underlying native elements.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCaption,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
interface Invoice {
|
||||
readonly id: string;
|
||||
readonly status: string;
|
||||
readonly amount: string;
|
||||
}
|
||||
|
||||
export function InvoiceTable({ invoices }: { invoices: Invoice[] }) {
|
||||
return (
|
||||
<Table>
|
||||
<TableCaption>A list of recent invoices.</TableCaption>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>ID</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead className="text-end">Amount</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{invoices.map((invoice) => (
|
||||
<TableRow key={invoice.id}>
|
||||
<TableCell>{invoice.id}</TableCell>
|
||||
<TableCell>{invoice.status}</TableCell>
|
||||
<TableCell className="text-end">
|
||||
{invoice.amount}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableFooter,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
export function OrderTotals({
|
||||
orders,
|
||||
grandTotal,
|
||||
}: {
|
||||
orders: Array<{ id: string; line: string; amount: number }>;
|
||||
grandTotal: number;
|
||||
}) {
|
||||
return (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Order</TableHead>
|
||||
<TableHead>Line</TableHead>
|
||||
<TableHead className="text-end">Amount</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{orders.map((order) => (
|
||||
<TableRow key={order.id}>
|
||||
<TableCell>{order.id}</TableCell>
|
||||
<TableCell>{order.line}</TableCell>
|
||||
<TableCell className="text-end">
|
||||
${order.amount.toFixed(2)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TableCell colSpan={2}>Total</TableCell>
|
||||
<TableCell className="text-end">
|
||||
${grandTotal.toFixed(2)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Table>`
|
||||
|
||||
`forwardRef<HTMLTableElement, TableProps>` — root `<table>` wrapped in `<div className="relative w-full overflow-auto">`.
|
||||
|
||||
### `<TableHeader>`, `<TableBody>`, `<TableFooter>`
|
||||
|
||||
`forwardRef<HTMLTableSectionElement, …>` — `<thead>`, `<tbody>`, `<tfoot>` row groups.
|
||||
|
||||
### `<TableRow>`
|
||||
|
||||
`forwardRef<HTMLTableRowElement, TableRowProps>` — `<tr>`. Hover + `data-state="selected"` background.
|
||||
|
||||
### `<TableHead>`
|
||||
|
||||
`forwardRef<HTMLTableCellElement, TableHeadProps>` — `<th>` header cell.
|
||||
|
||||
### `<TableCell>`
|
||||
|
||||
`forwardRef<HTMLTableCellElement, TableCellProps>` — `<td>` body cell.
|
||||
|
||||
### `<TableCaption>`
|
||||
|
||||
`forwardRef<HTMLTableCaptionElement, TableCaptionProps>` — `<caption>` rendered below the table.
|
||||
|
||||
#### Errors
|
||||
|
||||
The Table components do NOT call `useTranslation()` so they do not interact with the i18n provider. Children carrying user-perceivable text must be resolved at the call site.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis intentionally exposes NO `variant` axis on any sub-component — tables are pure-data surfaces and every chassis table should look the same. Consumers wanting different surfaces (zebra striping, dense layouts) override at the call site via `className` or register `variantOverrides` on their theme plugin.
|
||||
- The root wraps the native `<table>` in `<div className="relative w-full overflow-auto">` so wide tables scroll horizontally without breaking the page layout. This means refs forwarded to `<Table>` reach the inner `<table>`, NOT the outer `<div>`.
|
||||
- `<TableCaption>` always renders below the table thanks to `caption-bottom` on the root. Always include a caption (visually-hidden via `className="sr-only"` is acceptable) so screen-readers announce the table's purpose; tables without captions are unintelligible to screen-reader users.
|
||||
- `text-start` on `<TableHead>` uses Tailwind's logical-property class so the alignment flips automatically in RTL layouts. The same applies to `text-end` for right-aligned numeric columns — never use `text-left` / `text-right` in chassis-rendered tables.
|
||||
- The chassis does NOT inject any data-cell-binding behaviour. For a headless data-table with sorting, pagination, and selection on top of these primitives, use `<DataTable>` (which renders into the same `<Table>` family).
|
||||
@@ -0,0 +1,54 @@
|
||||
# Table Variants
|
||||
|
||||
## Purpose
|
||||
|
||||
`tableVariants` and its eight sub-component cva resolvers (`tableHeaderVariants`, `tableBodyVariants`, `tableFooterVariants`, `tableRowVariants`, `tableHeadVariants`, `tableCellVariants`, `tableCaptionVariants`) drive the chassis `<Table>` family. There are no exposed axes — typography, spacing, and border treatment are baked into single canonical class strings so every chassis table renders consistently across consumers. Theming flows through CSS variables (`bg-muted/50` on the footer, `text-muted-foreground` on header cells and captions) rather than through cva variants.
|
||||
|
||||
## Features
|
||||
|
||||
- Fixed typography rhythm — `text-sm` body, `font-medium text-muted-foreground` headers.
|
||||
- Logical-property text alignment (`text-start`) on header cells so RTL renders correctly.
|
||||
- Hover + selected (`data-state="selected"`) row states on `tableRowVariants`.
|
||||
- Footer surface uses `bg-muted/50 font-medium` to read as a summary row.
|
||||
- Caption surface uses `text-muted-foreground` and sits below the table (`caption-bottom`).
|
||||
- `[&:has([role=checkbox])]:pe-0` on header / cell variants kills the inline-end padding when the cell hosts a checkbox so the checkbox aligns flush.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { tableCellVariants, tableHeadVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const headClass = tableHeadVariants();
|
||||
const cellClass = tableCellVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { cva } from "class-variance-authority";
|
||||
import { tableRowVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const stripedRow = cva(
|
||||
[tableRowVariants(), "even:bg-muted/30"].join(" ")
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
Each cva resolver returns a static class string — no options. The module exports:
|
||||
|
||||
- `tableVariants()` — root `<table>`
|
||||
- `tableHeaderVariants()` — `<thead>`
|
||||
- `tableBodyVariants()` — `<tbody>`
|
||||
- `tableFooterVariants()` — `<tfoot>` summary row group
|
||||
- `tableRowVariants()` — `<tr>`
|
||||
- `tableHeadVariants()` — `<th>` header cell
|
||||
- `tableCellVariants()` — `<td>` body cell
|
||||
- `tableCaptionVariants()` — `<caption>` accessible label
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis intentionally exposes NO `variant` axis — tables are pure-data surfaces and every chassis table should look the same. Consumers wanting a different surface (zebra striping, dense layouts) override at the call site via `className` or register a `variantOverrides` on their theme plugin.
|
||||
- `caption-bottom` on the root keeps the caption beneath the table even though `<caption>` precedes the body in DOM order — this matches conventional reading order while keeping the markup semantic.
|
||||
- The footer's `[&>tr]:last:border-b-0` rule strips the bottom border on the last footer row so it does not double up against a containing border.
|
||||
- `[&:has([role=checkbox])]:pe-0` is applied to both `tableHeadVariants` and `tableCellVariants` so checkbox-bearing cells (typical for selectable rows) align flush. This is the same pattern shadcn ships upstream.
|
||||
141
docs/ui/react/react-ui/src/components/tabs/tabs.md
Normal file
141
docs/ui/react/react-ui/src/components/tabs/tabs.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# Tabs
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - tabs.variants — `tabsVariants`, `tabsListVariants`,…
|
||||
- [tabs.variants](./tabs.variants.md) — `tabsVariants`, `tabsListVariants`, `tabsTriggerVariants`, and `tabsContentVariants` are the cva (class-variance-author…
|
||||
- tabs.variants — `tabsVariants`, `tabsListVariants`,…
|
||||
|
||||
- [tabs.variants](./tabs.variants.md) — `tabsVariants`, `tabsListVariants`, `tabsTriggerVariants`, and `tabsContentVariants` are the cva (class-variance-author…
|
||||
|
||||
- tabs.variants — `tabsVariants`, `tabsListVariants`,…
|
||||
|
||||
- [tabs.variants](./tabs.variants.md) — `tabsVariants`, `tabsListVariants`, `tabsTriggerVariants`, and `tabsContentVariants` are the cva (class-variance-author…
|
||||
|
||||
- tabs.variants — `tabsVariants`, `tabsListVariants`,…
|
||||
|
||||
- [tabs.variants](./tabs.variants.md) — `tabsVariants`, `tabsListVariants`, `tabsTriggerVariants`, and `tabsContentVariants` are the cva (class-variance-author…
|
||||
- [tabs.variants](./tabs.variants.md) — `tabsVariants`, `tabsListVariants`, `tabsTriggerVariants`, and `tabsContentVariants` are the cva (class-variance-author…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Tabs>` and its sub-components (`<TabsList>`, `<TabsTrigger>`, `<TabsContent>`) are the chassis tabbed-interface primitive built on `@radix-ui/react-tabs`. Radix supplies the WAI-ARIA tabs semantics (`role="tablist"`, `role="tab"`, `role="tabpanel"`, arrow-key roving focus, Home / End jump-to-edge, Enter / Space activation in `manual` mode, automatic activation in `automatic` mode), and the chassis layers cva slots and theme overrides on top.
|
||||
|
||||
**Composition rule (shadcn skill rule):** `<TabsTrigger>` MUST sit inside `<TabsList>`. Triggers placed directly inside `<Tabs>` will not participate in the roving-focus group because Radix wires the keyboard navigation to the `<TabsList>` element specifically.
|
||||
|
||||
## Features
|
||||
|
||||
- WAI-ARIA tabs semantics from Radix: `role="tablist"`, `role="tab"`, `role="tabpanel"`, `aria-selected`, `aria-orientation`.
|
||||
- Arrow-key roving focus (left/right for horizontal, up/down for vertical).
|
||||
- Home / End jump to the first / last tab.
|
||||
- `activationMode="automatic"` (default) activates on focus; `activationMode="manual"` requires Enter / Space.
|
||||
- `orientation="horizontal"` (default) or `orientation="vertical"` switches both layout and arrow-key direction.
|
||||
- Disabled triggers skip in keyboard navigation and ignore clicks.
|
||||
- Theme-driven cva overrides via `useThemedVariants("Tabs<Slot>")`.
|
||||
- All sub-components forward refs to their underlying Radix elements.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@suaveplan/react-ui";
|
||||
|
||||
export function AccountTabs() {
|
||||
return (
|
||||
<Tabs defaultValue="account">
|
||||
<TabsList>
|
||||
<TabsTrigger value="account">Account</TabsTrigger>
|
||||
<TabsTrigger value="password">Password</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="account">Account settings here.</TabsContent>
|
||||
<TabsContent value="password">Change password here.</TabsContent>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@suaveplan/react-ui";
|
||||
import { useState } from "react";
|
||||
|
||||
interface Props {
|
||||
readonly initialTab: "metrics" | "logs" | "alerts";
|
||||
readonly onTabChange?: (tab: string) => void;
|
||||
}
|
||||
|
||||
export function ControlledDashboardTabs({ initialTab, onTabChange }: Props) {
|
||||
const [tab, setTab] = useState<string>(initialTab);
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
value={tab}
|
||||
onValueChange={(next) => {
|
||||
setTab(next);
|
||||
onTabChange?.(next);
|
||||
}}
|
||||
orientation="vertical"
|
||||
activationMode="manual"
|
||||
>
|
||||
<TabsList aria-label="Dashboard sections">
|
||||
<TabsTrigger value="metrics">Metrics</TabsTrigger>
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
<TabsTrigger value="alerts">Alerts</TabsTrigger>
|
||||
</TabsList>
|
||||
<div className="flex-1">
|
||||
<TabsContent value="metrics">Metrics panel</TabsContent>
|
||||
<TabsContent value="logs" forceMount>
|
||||
Logs panel (always mounted to preserve scroll)
|
||||
</TabsContent>
|
||||
<TabsContent value="alerts">Alerts panel</TabsContent>
|
||||
</div>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Tabs>`
|
||||
|
||||
The Radix Root that owns the active-tab state machine.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `defaultValue` | `string` | _none_ | Initial active value (uncontrolled). |
|
||||
| `value` | `string` | _uncontrolled_ | Controlled active value. |
|
||||
| `onValueChange` | `(value: string) => void` | _none_ | Fires on tab activation. |
|
||||
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Layout and arrow-key direction. |
|
||||
| `activationMode` | `"automatic" \| "manual"` | `"automatic"` | Whether focus alone activates. |
|
||||
| `dir` | `"ltr" \| "rtl"` | _inherits_ | Reading direction (Radix infers from DOM). |
|
||||
|
||||
### `<TabsList>`
|
||||
|
||||
Renders `role="tablist"`. **Per the shadcn skill rule, every `<TabsTrigger>` MUST live inside this list.** Inherits `orientation` from the Root via `data-orientation`.
|
||||
|
||||
### `<TabsTrigger>`
|
||||
|
||||
Renders `role="tab"`. Required `value` identifies which `<TabsContent>` to surface. Active state keys off Radix's `data-state="active"`.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `value` | `string` | _required_ | Tab identifier. |
|
||||
| `disabled` | `boolean` | `false` | Skips in keyboard nav and ignores activation. |
|
||||
|
||||
### `<TabsContent>`
|
||||
|
||||
Renders `role="tabpanel"`. Required `value` identifies which trigger this panel pairs with.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `value` | `string` | _required_ | Pairs with `<TabsTrigger value={...}>`. |
|
||||
| `forceMount` | `boolean` | `false` | Keep the panel mounted even when inactive (preserves scroll, expensive sub-trees). |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chassis NEVER assigns a numeric `z-index` to the tabs — they sit in the document flow.
|
||||
- `motion-reduce:transition-none` collapses the trigger background transition for `prefers-reduced-motion: reduce`.
|
||||
- The `active` state is keyed off Radix's `data-state="active"` attribute, not a manual `isActive` prop — Radix is the source of truth for which tab is selected.
|
||||
- Vertical orientation switches the cva from `flex-col` to `flex-row gap-4` and the list itself from `h-10` to `flex-col h-auto` so the rail wraps multiple triggers cleanly on the side.
|
||||
- The `<TabsTrigger>` cva carries `focus-visible:ring-2 focus-visible:ring-ring` so the focus ring only appears for keyboard users (matching shadcn upstream's treatment).
|
||||
- `activationMode="manual"` is recommended when activation has a side effect (fetch, animation) — `automatic` mode would fire the side-effect on every arrow-key press as the focus walks.
|
||||
73
docs/ui/react/react-ui/src/components/tabs/tabs.variants.md
Normal file
73
docs/ui/react/react-ui/src/components/tabs/tabs.variants.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# Tabs Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`tabsVariants`, `tabsListVariants`, `tabsTriggerVariants`, and `tabsContentVariants` are the cva (class-variance-authority) configurations powering every `<Tabs>` slot. They are split from `tabs.tsx` into their own module so an ejected source copy under `npx shadcn@latest add @suaveplan/tabs` keeps the cva configurations in a dedicated file. Theme overrides flow through `useThemedVariants("Tabs<Slot>")`.
|
||||
|
||||
The Root and List cva both carry an `orientation` axis (`horizontal` / `vertical`) so vertical layouts get the right `flex-row gap-4` plus an auto-height list rail.
|
||||
|
||||
## Features
|
||||
|
||||
- Root: `flex-col` (horizontal — list above content) or `flex-row gap-4` (vertical — list left of content).
|
||||
- List: muted-background pill (`bg-muted p-1`) sized at `h-10` for horizontal; `flex-col h-auto` for vertical.
|
||||
- Trigger: small pill (`px-3 py-1.5`) with active state lifting to `bg-background shadow-sm` keyed off Radix's `data-state="active"`. Focus-visible ring; disabled opacity; `motion-reduce:transition-none`.
|
||||
- Content: top margin (`mt-2`) and matching focus ring so keyboard users land in the panel without a visual jolt.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { tabsTriggerVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const className = tabsTriggerVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { tabsListVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function PillList({
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
tabsListVariants({ orientation: "horizontal" }),
|
||||
"rounded-full",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `tabsVariants(props?: { orientation?: "horizontal" | "vertical" }): string`
|
||||
|
||||
Root wrapper class string. Defaults to `{ orientation: "horizontal" }`.
|
||||
|
||||
### `tabsListVariants(props?: { orientation?: "horizontal" | "vertical" }): string`
|
||||
|
||||
`<TabsList>` class string. Defaults to `{ orientation: "horizontal" }`.
|
||||
|
||||
### `tabsTriggerVariants(): string`
|
||||
|
||||
`<TabsTrigger>` class string. No axes — variation comes from the active / disabled `data-*` attributes Radix sets at runtime.
|
||||
|
||||
### `tabsContentVariants(): string`
|
||||
|
||||
`<TabsContent>` class string. No axes.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The trigger cva uses `data-[state=active]:bg-background` to drive the active-pill treatment — Radix is the source of truth for which trigger is active.
|
||||
- `focus-visible` (not `focus`) so the ring only appears for keyboard users.
|
||||
- `motion-reduce:transition-none` collapses the active-state transition for `prefers-reduced-motion: reduce`.
|
||||
- Per the shadcn skill rule, no numeric `z-index` is assigned anywhere — tabs sit in the document flow.
|
||||
103
docs/ui/react/react-ui/src/components/textarea/textarea.md
Normal file
103
docs/ui/react/react-ui/src/components/textarea/textarea.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# Textarea
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - textarea.variants — `textareaVariants` is the cva (…
|
||||
- [textarea.variants](./textarea.variants.md) — `textareaVariants` is the cva (class-variance-authority) configuration that powers every `<Textarea>` render. It is spl…
|
||||
- textarea.variants — `textareaVariants` is the cva (…
|
||||
|
||||
- [textarea.variants](./textarea.variants.md) — `textareaVariants` is the cva (class-variance-authority) configuration that powers every `<Textarea>` render. It is spl…
|
||||
|
||||
- textarea.variants — `textareaVariants` is the cva (…
|
||||
|
||||
- [textarea.variants](./textarea.variants.md) — `textareaVariants` is the cva (class-variance-authority) configuration that powers every `<Textarea>` render. It is spl…
|
||||
|
||||
- textarea.variants — `textareaVariants` is the cva (…
|
||||
|
||||
- [textarea.variants](./textarea.variants.md) — `textareaVariants` is the cva (class-variance-authority) configuration that powers every `<Textarea>` render. It is spl…
|
||||
- [textarea.variants](./textarea.variants.md) — `textareaVariants` is the cva (class-variance-authority) configuration that powers every `<Textarea>` render. It is spl…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Textarea>` is the chassis multi-line form-input primitive for `@suaveplan/react-ui`. It renders a native `<textarea>` whose visual variants flow through `useThemedVariants("Textarea", textareaVariants)` so an active `ThemePlugin` may extend the cva size axis without forking the component source. Refs are forwarded to the underlying element so consumers can focus, select, measure, or drive auto-resize manually.
|
||||
|
||||
The translation contract mirrors `<Input>` — the `placeholder` prop is typed as the branded `TranslationKey | undefined`, so consumers cannot pass a raw string literal. When provided, the chassis resolves the key via the active i18n at render time, keeping every user-perceivable string under translation control. When `undefined`, no `placeholder` attribute is written to the DOM.
|
||||
|
||||
Auto-resize is intentionally NOT included. Consumers who need it attach their own ref and recompute `style.height` against `scrollHeight` — a four-line wrapper outside the chassis surface that keeps `<Textarea>` a pure styling + a11y primitive.
|
||||
|
||||
## Features
|
||||
|
||||
- Three sizes (`sm`, `default`, `lg`) controlling `min-height` so consumers can drop a `size="lg"` into a notes field without manual class overrides.
|
||||
- `placeholder` typed as branded `TranslationKey | undefined`; resolved via active i18n at render time.
|
||||
- `data-invalid="true"` written when the `invalid` prop is `true` so the cva config's `data-[invalid=true]:border-destructive` rule activates.
|
||||
- `data-disabled="true"` mirrors the native `disabled` attribute so sibling `<Field>` containers can react via the shadcn data-attribute selector convention.
|
||||
- Refs forwarded to the underlying `<textarea>` for focus / measure / auto-resize integrations.
|
||||
- `motion-reduce:transition-none` collapses transitions for users with `prefers-reduced-motion`.
|
||||
- Theme-driven variant overrides via `useThemedVariants("Textarea", textareaVariants)` — themes may add brand-new sizes via `variantOverrides.Textarea`.
|
||||
- All native `TextareaHTMLAttributes` are forwarded so `onChange`, `onInput`, `rows`, `cols`, `maxLength` work as expected.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { Textarea } from "@suaveplan/react-ui";
|
||||
|
||||
export function NotesField() {
|
||||
return <Textarea defaultValue="" rows={4} />;
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { Textarea, Field, FieldLabel, FieldError } from "@suaveplan/react-ui";
|
||||
import { useState } from "react";
|
||||
|
||||
export function BiographyField() {
|
||||
const [value, setValue] = useState("");
|
||||
const tooLong = value.length > 500;
|
||||
|
||||
return (
|
||||
<Field invalid={tooLong}>
|
||||
<FieldLabel htmlFor="bio">Biography</FieldLabel>
|
||||
<Textarea
|
||||
id="bio"
|
||||
size="lg"
|
||||
rows={6}
|
||||
value={value}
|
||||
invalid={tooLong}
|
||||
aria-invalid={tooLong}
|
||||
onChange={(e) => setValue(e.currentTarget.value)}
|
||||
placeholder="react-ui.empty.noResults"
|
||||
/>
|
||||
{tooLong ? <FieldError>Too long.</FieldError> : null}
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Textarea>`
|
||||
|
||||
`forwardRef<HTMLTextAreaElement, TextareaProps>` — renders a `<textarea>` with the chassis cva classes applied.
|
||||
|
||||
#### `TextareaProps`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Visual variant controlling `min-height`. |
|
||||
| `placeholder` | `TranslationKey` | _none_ | Translation key resolved via active i18n. |
|
||||
| `invalid` | `boolean` | `false` | Drives `data-invalid="true"` on the rendered element. |
|
||||
| `disabled` | `boolean` | `false` | Drives `data-disabled="true"` and the native `disabled` attribute. |
|
||||
| _all native_ `TextareaHTMLAttributes<HTMLTextAreaElement>` | _native_ | _native_ | Forwarded as-is (minus `size` / `placeholder` which are typed above). |
|
||||
|
||||
#### `textareaVariants`
|
||||
|
||||
The bare cva resolver. Exported so external consumers can re-use the variants directly.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The variants module is intentionally separate (`textarea.variants.ts`) so an ejected source copy keeps the cva config in a dedicated file, mirroring the upstream shadcn convention.
|
||||
- Auto-resize is out of scope for this primitive. Consumers needing it integrate via the forwarded ref and a `useLayoutEffect` that sets `el.style.height = el.scrollHeight + 'px'` on every keystroke.
|
||||
- The chassis writes `data-disabled="true"` even though the native `disabled` already applies because sibling `<Field>` / `<InputGroup>` containers use the data-attribute selector to drive their own disabled-state styling without needing to walk the DOM.
|
||||
- The `invalid` prop is independent of `aria-invalid`; consumers who want screen-reader semantics must pass both. The chassis does not auto-mirror the boolean to the ARIA attribute because `aria-invalid` accepts `"grammar"` and `"spelling"` values that are domain-specific.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Textarea Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`textareaVariants` is the cva (class-variance-authority) configuration that powers every `<Textarea>` render. It is split from `textarea.tsx` into its own module so an ejected source copy under `npx shadcn@latest add @suaveplan/textarea` keeps the cva configuration in a dedicated file — mirroring the upstream shadcn convention. Theme overrides flow through `useThemedVariants("Textarea", textareaVariants)`, so the runtime cva resolver can be replaced or extended by an active `ThemePlugin` without touching the component source.
|
||||
|
||||
The module exports a single `textareaVariants` callable resolver tagged with the component name `"Textarea"` via `defineVariants`. Consumers wanting the bare resolver — for example to render the same variants on a non-`<textarea>` element — import `textareaVariants` directly and apply the result via `cn()` themselves.
|
||||
|
||||
## Features
|
||||
|
||||
- Three sizes (`sm` / `default` / `lg`) controlling `min-height` (60 / 80 / 120 px).
|
||||
- Border + shadow + focus ring matching the chassis `<Input>` typography.
|
||||
- `data-[invalid=true]:border-destructive` rule activates when the wrapped control sets `data-invalid="true"`.
|
||||
- `disabled:cursor-not-allowed disabled:opacity-50` for the native disabled state.
|
||||
- `motion-reduce:transition-none` for users with `prefers-reduced-motion: reduce`.
|
||||
- `placeholder:text-muted-foreground` for muted placeholder text.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { textareaVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const className = textareaVariants({ size: "default" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { textareaVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomTextarea(props: { className?: string }) {
|
||||
return (
|
||||
<textarea
|
||||
className={cn(
|
||||
textareaVariants({ size: "lg" }),
|
||||
"resize-none",
|
||||
props.className
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `textareaVariants(props?: { size?: "sm" | "default" | "lg" }): string`
|
||||
|
||||
Returns the cva-merged class string for the supplied size. Omitted props fall back to the cva `defaultVariants` (`size: "default"`).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The resolver is tagged with `componentName: "Textarea"` (non-enumerable, non-writable) via `defineVariants` so debug tooling can identify the cva instance at runtime.
|
||||
- The cva config is intentionally narrow — only one variant axis (`size`). Visual variants (outlined / filled / etc.) are intentionally NOT in the chassis surface; themes that need them register via `variantOverrides.Textarea`.
|
||||
- Tokens reference theme-driven CSS custom properties (`border-input`, `bg-transparent`, `text-muted-foreground`) so the actual colour values come from the active `ThemePlugin.cssVariables` payload.
|
||||
- Auto-resize is intentionally NOT a variant — it is a runtime behaviour driven by ref + `useLayoutEffect`, not a class.
|
||||
@@ -0,0 +1,132 @@
|
||||
# `<ThemeToggle>`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - theme-toggle.variants — Cva slot for the chassis `<…
|
||||
- [theme-toggle.variants](./theme-toggle.variants.md) — Cva slot for the chassis `<ThemeToggle>` icon-button. Mirrors the shape of `<Button variant="outline" size="icon">` so…
|
||||
- theme-toggle.variants — Cva slot for the chassis `<…
|
||||
|
||||
- [theme-toggle.variants](./theme-toggle.variants.md) — Cva slot for the chassis `<ThemeToggle>` icon-button. Mirrors the shape of `<Button variant="outline" size="icon">` so…
|
||||
|
||||
- theme-toggle.variants — Cva slot for the chassis `<…
|
||||
|
||||
- [theme-toggle.variants](./theme-toggle.variants.md) — Cva slot for the chassis `<ThemeToggle>` icon-button. Mirrors the shape of `<Button variant="outline" size="icon">` so…
|
||||
|
||||
- theme-toggle.variants — Cva slot for the chassis `<…
|
||||
|
||||
- [theme-toggle.variants](./theme-toggle.variants.md) — Cva slot for the chassis `<ThemeToggle>` icon-button. Mirrors the shape of `<Button variant="outline" size="icon">` so…
|
||||
- [theme-toggle.variants](./theme-toggle.variants.md) — Cva slot for the chassis `<ThemeToggle>` icon-button. Mirrors the shape of `<Button variant="outline" size="icon">` so…
|
||||
|
||||
## Purpose
|
||||
|
||||
Chassis headless theme switcher rendered as an icon button. Wraps
|
||||
`useTheme()` (the chassis hook over `<ThemeProvider>`) and the theme
|
||||
registry under the active provider; clicking the button advances the
|
||||
active theme according to the configured `mode`. The toggle is the
|
||||
primary UX surface consumers drop into a header / sidebar / settings
|
||||
panel for "let me pick light vs. dark" without mounting a custom
|
||||
`<ThemeSwitcher>` render-prop.
|
||||
|
||||
## Features
|
||||
|
||||
- Three modes: `toggle` (light ↔ dark), `cycle` (advance through
|
||||
every registered theme), `system-aware` (cycle plus a virtual
|
||||
`system` step that defers to OS preference).
|
||||
- Icon-only — chassis injects a translated `aria-label` (defaults to
|
||||
`react-ui.themeToggle.label` → "Toggle theme") so the icon button
|
||||
carries an accessible name.
|
||||
- Per-theme icon mapping: `<SunIcon>` / `<MoonIcon>` /
|
||||
`<ContrastIcon>` / `<MonitorIcon>` for chassis-shipped themes;
|
||||
custom themes fall back to `<MonitorIcon>`.
|
||||
- Reduced-motion: the underlying `<ThemeProvider>` honours
|
||||
`prefers-reduced-motion` already; the toggle adds no motion of its
|
||||
own.
|
||||
- `forwardRef` to the underlying `<button>` element.
|
||||
- Cva slot `ThemeToggle` registered into `ThemableVariants` for
|
||||
brand-theme overrides.
|
||||
- Pure helper `pickNextThemeName(mode, current, registered)` exposed
|
||||
for callers that want to compute the next theme name without
|
||||
triggering the click handler (e.g. URL synchronisation).
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { ThemeToggle } from "@suaveplan/react-ui";
|
||||
|
||||
function Header(): JSX.Element {
|
||||
return (
|
||||
<header className="flex items-center justify-end p-4">
|
||||
<ThemeToggle />
|
||||
</header>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { ThemeToggle } from "@suaveplan/react-ui";
|
||||
|
||||
function SettingsPanel(): JSX.Element {
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<ThemeToggle mode="toggle" />
|
||||
<ThemeToggle mode="cycle" />
|
||||
<ThemeToggle
|
||||
mode="system-aware"
|
||||
ariaLabelKey="react-ui.themeToggle.system"
|
||||
onClick={(event) => {
|
||||
if (analyticsBlocked) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<ThemeToggle>` props
|
||||
|
||||
- `mode?: 'toggle' | 'cycle' | 'system-aware'` — defaults to
|
||||
`'toggle'`.
|
||||
- `ariaLabelKey?: TranslationKey` — defaults to
|
||||
`react-ui.themeToggle.label`.
|
||||
- `className?: string` — merged with the cva-resolved class string.
|
||||
- `onClick?: (event: MouseEvent) => void` — fires BEFORE the
|
||||
chassis swaps the theme. Calling `event.preventDefault()` cancels
|
||||
the swap.
|
||||
- All other `ButtonHTMLAttributes` (id, data-*, disabled, …) are
|
||||
forwarded.
|
||||
|
||||
Refs are forwarded to the underlying `<button>` element.
|
||||
|
||||
### `pickNextThemeName(mode, current, registered): string`
|
||||
|
||||
Pure helper exposed for callers that want to compute the next theme
|
||||
name without triggering the click handler. The chassis itself uses
|
||||
this internally; tests pin the per-mode behaviour via this function
|
||||
so the React rendering layer does not need a fresh provider per
|
||||
case.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The toggle MUST live inside a `<ThemeProvider>` because it reads
|
||||
`useTheme()`; outside a provider it throws `REACT_UI.NO_PROVIDER`.
|
||||
- The icon mapping deliberately accepts both `highContrast` (camelCase
|
||||
— chassis convention) and `high-contrast` (kebab-case — older
|
||||
upstream convention) so brand themes registered under either name
|
||||
pick up the `<ContrastIcon>` automatically.
|
||||
- The system-aware mode does NOT directly read
|
||||
`prefers-color-scheme` — the underlying `<ThemeProvider>` already
|
||||
paints the system preference when no explicit theme is active, so
|
||||
the virtual `system` step in the cycle simply defers to the
|
||||
provider's existing behaviour.
|
||||
- The `onClick` consumer hook fires BEFORE the theme swap so callers
|
||||
can call `event.preventDefault()` to cancel the swap (useful for
|
||||
feature-flag gates or analytics tracking that wants to short-circuit
|
||||
the action).
|
||||
- Refs forward to the button element rather than the icon so callers
|
||||
can `focus()` the toggle programmatically.
|
||||
@@ -0,0 +1,64 @@
|
||||
# `theme-toggle.variants`
|
||||
|
||||
## Purpose
|
||||
|
||||
Cva slot for the chassis `<ThemeToggle>` icon-button. Mirrors the
|
||||
shape of `<Button variant="outline" size="icon">` so the toggle reads
|
||||
as a tertiary chrome control (the kind of button you put in a header
|
||||
or sidebar, not the primary CTA).
|
||||
|
||||
## Features
|
||||
|
||||
- Single `ThemeToggle` slot keyed by the `mode` axis (`toggle` /
|
||||
`cycle` / `system-aware`). Currently every mode uses the same
|
||||
visual treatment; the axis is reserved so brand themes can layer
|
||||
per-mode tweaks (e.g. a wider button on the system-aware mode that
|
||||
carries an extra status indicator).
|
||||
- Outline-button shape (border + background + shadow) so the toggle
|
||||
reads against any background colour.
|
||||
- `motion-reduce:transition-none` so users with reduced-motion
|
||||
preference see an instant state swap rather than the colour fade.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { themeToggleVariants } from "@suaveplan/react-ui/components/theme-toggle/theme-toggle";
|
||||
|
||||
const className = themeToggleVariants({ mode: "toggle" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { defineVariants } from "@suaveplan/react-ui/variants";
|
||||
import type { ThemePlugin } from "@suaveplan/react-ui";
|
||||
|
||||
const brandTheme: ThemePlugin = {
|
||||
name: "brand",
|
||||
tokens: { /* ... */ },
|
||||
variantOverrides: {
|
||||
ThemeToggle: defineVariants("ThemeToggle", "rounded-full p-0", {
|
||||
variants: { mode: { toggle: "", cycle: "", "system-aware": "w-12" } },
|
||||
defaultVariants: { mode: "toggle" },
|
||||
}),
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `themeToggleVariants({ mode })`: returns the merged class string.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The empty `mode` variants are intentional — the chassis ships ONE
|
||||
visual treatment so the cross-mode UX feels identical across the
|
||||
three modes. The axis is present at the cva level so brand themes do
|
||||
not have to introduce their own discriminator when they want
|
||||
per-mode treatment; they declare an override on
|
||||
`themeToggleVariants` and continue to receive the chassis-managed
|
||||
`mode` prop. Reserving the axis up-front also means consumers do not
|
||||
have to re-declare the prop on a per-pack basis when a custom
|
||||
treatment is needed: the override mechanism in the theme plugin
|
||||
contract takes care of slotting the brand-specific variants into the
|
||||
existing prop surface, so the consumer call site stays identical.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user