@kb.ui/react 0.10.3 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +130 -708
  2. package/dist/components/Accordion/Accordion.d.ts +33 -0
  3. package/dist/components/Avatar/Avatar.d.ts +19 -0
  4. package/dist/components/Code/Code.d.ts +26 -3
  5. package/dist/components/Code/tokenize.d.ts +5 -0
  6. package/dist/components/CodeBlock/CodeBlock.d.ts +10 -0
  7. package/dist/components/Collection/Collection.d.ts +99 -0
  8. package/dist/components/Collection/useCollection.d.ts +66 -0
  9. package/dist/components/CollectionList/CollectionList.d.ts +12 -0
  10. package/dist/components/CollectionRow/CollectionRow.d.ts +17 -0
  11. package/dist/components/Combobox/Combobox.d.ts +14 -2
  12. package/dist/components/CopyableText/CopyableText.d.ts +38 -0
  13. package/dist/components/CopyableText/content.d.ts +14 -0
  14. package/dist/components/DescriptionList/DescriptionList.d.ts +20 -0
  15. package/dist/components/Dialog/Dialog.d.ts +3 -1
  16. package/dist/components/Disclosure/Disclosure.d.ts +21 -0
  17. package/dist/components/EmptyState/EmptyState.d.ts +30 -0
  18. package/dist/components/Grid/Grid.d.ts +17 -6
  19. package/dist/components/Image/Image.d.ts +20 -0
  20. package/dist/components/Link/Link.d.ts +15 -2
  21. package/dist/components/Popover/Popover.d.ts +55 -0
  22. package/dist/components/Skeleton/Skeleton.d.ts +32 -0
  23. package/dist/components/Spinner/Spinner.d.ts +26 -0
  24. package/dist/components/Table/Table.d.ts +65 -0
  25. package/dist/components/Tabs/Tabs.d.ts +69 -0
  26. package/dist/components/Tabs/index.d.ts +1 -0
  27. package/dist/components/Thumbnail/Thumbnail.d.ts +14 -0
  28. package/dist/components/Toast/Toast.d.ts +72 -0
  29. package/dist/index.d.ts +29 -4
  30. package/dist/index.js +3510 -1232
  31. package/dist/index.js.map +1 -1
  32. package/dist/styles.css +1 -1
  33. package/dist/utils/CollapsiblePanel.d.ts +10 -0
  34. package/dist/utils/useClipboard.d.ts +28 -0
  35. package/docs/components/Accordion.md +66 -0
  36. package/docs/components/Avatar.md +80 -0
  37. package/docs/components/Badge.md +37 -0
  38. package/docs/components/Button.md +46 -0
  39. package/docs/components/Callout.md +41 -0
  40. package/docs/components/Card.md +41 -0
  41. package/docs/components/Checkbox.md +60 -0
  42. package/docs/components/Code.md +77 -0
  43. package/docs/components/CodeBlock.md +77 -0
  44. package/docs/components/Collection.md +183 -0
  45. package/docs/components/CollectionList.md +44 -0
  46. package/docs/components/CollectionRow.md +62 -0
  47. package/docs/components/Combobox.md +88 -0
  48. package/docs/components/Container.md +41 -0
  49. package/docs/components/CopyableText.md +93 -0
  50. package/docs/components/DescriptionList.md +61 -0
  51. package/docs/components/Dialog.md +84 -0
  52. package/docs/components/Disclosure.md +53 -0
  53. package/docs/components/EmptyState.md +50 -0
  54. package/docs/components/ErrorSummary.md +46 -0
  55. package/docs/components/Fieldset.md +42 -0
  56. package/docs/components/FileInput.md +74 -0
  57. package/docs/components/FormActions.md +45 -0
  58. package/docs/components/Grid.md +66 -0
  59. package/docs/components/Heading.md +40 -0
  60. package/docs/components/Image.md +62 -0
  61. package/docs/components/Inline.md +42 -0
  62. package/docs/components/Input.md +82 -0
  63. package/docs/components/Link.md +52 -0
  64. package/docs/components/Menu.md +44 -0
  65. package/docs/components/MultiSelect.md +59 -0
  66. package/docs/components/NumberField.md +65 -0
  67. package/docs/components/Panel.md +145 -0
  68. package/docs/components/Popover.md +89 -0
  69. package/docs/components/Progress.md +37 -0
  70. package/docs/components/RadioGroup.md +69 -0
  71. package/docs/components/ScrollArea.md +40 -0
  72. package/docs/components/Select.md +71 -0
  73. package/docs/components/Separator.md +40 -0
  74. package/docs/components/Skeleton.md +46 -0
  75. package/docs/components/Spinner.md +40 -0
  76. package/docs/components/Split.md +47 -0
  77. package/docs/components/Stat.md +34 -0
  78. package/docs/components/Status.md +47 -0
  79. package/docs/components/Switch.md +73 -0
  80. package/docs/components/Table.md +105 -0
  81. package/docs/components/Tabs.md +89 -0
  82. package/docs/components/Text.md +36 -0
  83. package/docs/components/Textarea.md +47 -0
  84. package/docs/components/Thumbnail.md +66 -0
  85. package/docs/components/Toast.md +130 -0
  86. package/docs/components/Toolbar.md +39 -0
  87. package/docs/components/Tooltip.md +42 -0
  88. package/package.json +3 -2
@@ -0,0 +1,183 @@
1
+ # Collection
2
+
3
+ Work through record sets using search, facet filters, sortable columns, page selection, pagination and bulk actions. `Collection` composes a native table and kbui controls. `useCollection` provides the same state contract for custom cards or lists. `Collection.Pagination` and `Collection.BulkActions` also work independently.
4
+
5
+ [All components](../../README.md#components) · [Usage](#usage) · [Examples](#more-examples) · [Reference](#reference) · [Live examples and accessibility](https://kbui.kcb.pro/storybook/?path=/docs/components-collection--docs)
6
+
7
+ ## Usage
8
+
9
+ ```tsx
10
+ import "@kb.ui/react/styles.css";
11
+ import { Collection } from "@kb.ui/react";
12
+ import { useState } from "react";
13
+
14
+ type Project = { id: string; name: string; status: string };
15
+ const initial: Project[] = [
16
+ { id: "web", name: "Website refresh", status: "Active" },
17
+ { id: "help", name: "Help center", status: "Paused" },
18
+ ];
19
+
20
+ export function Projects() {
21
+ const [items, setItems] = useState(initial);
22
+ return (
23
+ <Collection
24
+ label="Projects"
25
+ items={items}
26
+ getKey={item => item.id}
27
+ getItemLabel={item => item.name}
28
+ columns={[
29
+ { key: "name", header: "Project", cell: item => item.name,
30
+ compare: (a, b) => a.name.localeCompare(b.name) },
31
+ { key: "status", header: "Status", cell: item => item.status },
32
+ ]}
33
+ matches={(item, query, filters) =>
34
+ item.name.toLowerCase().includes(query.toLowerCase()) &&
35
+ (!filters.status || item.status === filters.status)}
36
+ filters={[{ key: "status", label: "Status", options: [
37
+ { value: "Active", label: "Active" },
38
+ { value: "Paused", label: "Paused" },
39
+ ] }]}
40
+ bulkActions={[{ key: "archive", label: "Archive", onAction: async keys => {
41
+ setItems(current => current.filter(item => !keys.includes(item.id)));
42
+ } }]}
43
+ />
44
+ );
45
+ }
46
+ ```
47
+
48
+ ## More examples
49
+
50
+ - [Large collection](https://kbui.kcb.pro/storybook/?path=/story/components-collection--large-collection): Search and filter 120 records; paused records cannot be selected.
51
+ - [Server pagination](https://kbui.kcb.pro/storybook/?path=/story/components-collection--server-pagination): Controlled state with application-owned data processing.
52
+ - [Empty](https://kbui.kcb.pro/storybook/?path=/story/components-collection--empty), [loading](https://kbui.kcb.pro/storybook/?path=/story/components-collection--loading), and [fetch error](https://kbui.kcb.pro/storybook/?path=/story/components-collection--fetch-error): Explicit result states.
53
+ - [Bulk failure](https://kbui.kcb.pro/storybook/?path=/story/components-collection--bulk-failure): Selection survives rejection so users can retry.
54
+
55
+ ## Reference
56
+
57
+ ### Collection props
58
+
59
+ | Prop | Type | Default | Contract |
60
+ | --- | --- | --- | --- |
61
+ | `label` | `string` | required | Visible caption and accessible region name |
62
+ | `items` | `readonly T[]` | required | Complete dataset in client mode; current response page in server mode |
63
+ | `getKey` | `(item: T) => string` | required | Unique stable record ID; duplicates in supplied rows throw |
64
+ | `getItemLabel` | `(item: T) => string` | required | Meaningful accessible checkbox name |
65
+ | `columns` | `readonly CollectionColumn<T>[]` | required | Ordered unique keys, string headers and cell renderers |
66
+ | `mode` | `client` / `server` | `client` | Server mode skips all local filtering, sorting and slicing |
67
+ | `totalItems` | `number` | client derives it | Required in server mode; known filtered total, non-negative safe integer |
68
+ | `matches` | `(item, query, filters) => boolean` | unset | Client predicate; also enables client search UI |
69
+ | `compare` | `(a, b, sort) => number` | column comparator | Optional hook-level comparison override; return ascending order |
70
+ | `filters` | `readonly CollectionFilter[]` | `[]` | Facet key, visible label and nonempty option values; empty string means All |
71
+ | `isItemDisabled` | `(item: T) => boolean` | unset | Prevent individual and page selection of ineligible supplied records |
72
+ | `state` / `onStateChange` | `CollectionState` / `(next) => void` | uncontrolled | Parent applies complete next state; suitable for URL or server ownership |
73
+ | `defaultState` | `Partial<CollectionState>` | see state table | Initial uncontrolled state; later changes do not reinitialize it |
74
+ | `selectable` | `boolean` | `true` | Show row/page checkboxes and selection actions |
75
+ | `bulkActions` | `readonly CollectionBulkAction[]` | `[]` | Unique key, label, `onAction(keys)`, optional disabled flag |
76
+ | `loading` | `boolean` | `false` | Mark busy, suppress stale rows and disable workflow controls |
77
+ | `error` | `ReactNode` | unset | Fetch failure alert, suppress rows and disable workflow controls |
78
+ | `empty` | `ReactNode` | `No matching records` | Zero-result content; include a useful next step |
79
+ | `toolbar` | `ReactNode` | unset | Extra application-owned controls, retry or creation actions |
80
+ | `className`, `style`, `ref` | native section props | unset | Root customization and element reference |
81
+ | `classNames` | slot classes | unset | `toolbar`, `filters`, `viewport`, `table`, `caption`, `head`, `cell`, `row`, `empty`, `status`, `bulk`, `pagination` |
82
+
83
+ A column has `key`, `header`, `cell(item)`, optional ascending `compare(a, b)`, and optional `sortable` for server-only sort requests. Sorting cycles ascending, descending, input order. Ties preserve supplied order. Sorting never mutates the supplied array. Search and facet interpretation belong to `matches`; kbui does not guess text from rendered React nodes. Server mode always exposes search and emits its value unchanged.
84
+
85
+ ### State and transitions
86
+
87
+ | State field | Default | Transition contract |
88
+ | --- | --- | --- |
89
+ | `query` | `""` | `setQuery` resets page to one and clears selection |
90
+ | `filters` | `{}` | `setFilter(key, value)` preserves other facets, resets page and clears selection |
91
+ | `sort` | `null` | `{ key, direction: "ascending" or "descending" }`; `setSort` resets page, preserves selection |
92
+ | `page` | `1` | One-based positive safe integer; effective page clamps when results shrink |
93
+ | `pageSize` | `10` | Positive safe integer; `setPageSize` resets page and preserves selection |
94
+ | `selectedKeys` | `[]` | Explicit unique IDs; never an implicit selection of every remote result |
95
+
96
+ Client mode processes records in this order: predicate, stable comparison, page slice. Removed or newly disabled IDs disappear from effective selection. Server mode preserves off-page IDs; eligibility for unseen records remains the application’s responsibility. Both modes remove disabled IDs present in the current response. Zero results have one empty page. Invalid counts, pages and sizes throw `RangeError` rather than emitting broken navigation.
97
+
98
+ `state` is controlled when supplied. Each controlled proposal starts from the latest accepted props; a rejected proposal cannot affect a later interaction. Combine coordinated controlled changes in the parent before applying one next state. Uncontrolled commands in one event compose sequentially, including derived page bounds and page selection. Retained methods consult the latest committed dataset, eligibility rules, callbacks and accepted state, so asynchronous completion removes only its submitted IDs. Rendered state remains owned by the parent until it accepts `onStateChange`. Normalized selection and clamped page never trigger onStateChange during data refresh; the next user transition emits normalized state. Uncontrolled state retains those corrections so deleted/reintroduced IDs stay deselected and growing datasets do not restore an obsolete page. Controlled parent stores should reconcile their canonical state when external data changes. Use immutable arrays/objects and stable IDs across refreshes.
99
+
100
+ ### useCollection
101
+
102
+ Accepts `items`, `getKey`, `isItemDisabled`, `matches`, `compare`, `mode`, `totalItems`, `state`, `defaultState`, and `onStateChange`. It renders no DOM and performs no fetching or mutations.
103
+
104
+ | Result | Contract |
105
+ | --- | --- |
106
+ | `state`, `items`, `totalItems`, `pageCount` | Effective normalized state, visible rows and derived page metadata |
107
+ | `pageSelection` | `none`, `some`, or `all` among eligible current-page records |
108
+ | `selectableCount` | Eligible current-page record count |
109
+ | `setQuery`, `setFilter`, `setSort`, `setPage`, `setPageSize` | Apply transitions described above |
110
+ | `toggle(key, checked)` | Change selection for a supplied eligible record; unknown or disabled IDs are ignored |
111
+ | `selectPage(checked)` | Add/remove eligible page IDs while preserving off-page IDs |
112
+ | `clearSelection()` | Clear all selected IDs |
113
+ | `removeSelection(keys)` | Remove only specified IDs, suitable for successful submitted snapshots |
114
+
115
+ For a custom list, reuse the controller and compound controls:
116
+
117
+ ```tsx
118
+ import "@kb.ui/react/styles.css";
119
+ import { Checkbox, Collection, useCollection } from "@kb.ui/react";
120
+ import { useState } from "react";
121
+
122
+ export function RecordList() {
123
+ const [items, setItems] = useState([
124
+ { id: "web", name: "Website refresh" },
125
+ { id: "help", name: "Help center" },
126
+ ]);
127
+ const [pending, setPending] = useState(false);
128
+ const collection = useCollection({ items, getKey: item => item.id });
129
+ return (
130
+ <section aria-label="Project list">
131
+ <ul>
132
+ {collection.items.map(item => (
133
+ <li key={item.id}>
134
+ <Checkbox label={item.name} disabled={pending}
135
+ checked={collection.state.selectedKeys.includes(item.id)}
136
+ onCheckedChange={checked => collection.toggle(item.id, checked)} />
137
+ </li>
138
+ ))}
139
+ </ul>
140
+ <Collection.BulkActions selectedKeys={collection.state.selectedKeys}
141
+ onClear={collection.clearSelection} onSuccess={collection.removeSelection}
142
+ onPendingChange={setPending} actions={[{ key: "archive", label: "Archive",
143
+ onAction: keys => setItems(current => current.filter(item => !keys.includes(item.id))) }]} />
144
+ <Collection.Pagination page={collection.state.page}
145
+ pageSize={collection.state.pageSize} totalItems={collection.totalItems}
146
+ onPageChange={collection.setPage} disabled={pending} />
147
+ </section>
148
+ );
149
+ }
150
+ ```
151
+
152
+ ### Standalone controls
153
+
154
+ | Component | Required props | Optional props and behavior |
155
+ | --- | --- | --- |
156
+ | `Collection.Pagination` | `page`, `pageSize`, `totalItems`, `onPageChange` | `onPageSizeChange`, `pageSizes` (10, 25, 50), `disabled`, native nav props/ref. Includes current page size, emits bounded page requests; caller resets page when size changes. |
157
+ | `Collection.BulkActions` | `selectedKeys`, `actions`, `onClear` | `onSuccess(keys)`, `onPendingChange(boolean)`, `disabled`, native fieldset props/ref. Caller owns selection; omitted onSuccess preserves it. |
158
+
159
+ ### Bulk-action boundary
160
+
161
+ Actions receive a copied, deduplicated ID array captured at submission. Selection counts use the same distinct IDs. Buttons remain mounted during selection changes. Empty selections and disabled actions cannot submit; an immediate lock prevents repeated submission before React rerenders. Pending actions disable other workflow controls in `Collection`. Success handlers come from the latest committed render; suspended renders cannot replace an active operation’s handler. A resolved operation removes only submitted IDs; a rejected operation announces a generic failure and retains selection. Clear selection dismisses the displayed failure.
162
+
163
+ Applications own permission checks, destructive confirmation, server authorization, cancellation, idempotency, optimistic updates, undo, auditing and focus recovery after record removal. A resolved promise means the complete snapshot succeeded. For partial success, reconcile affected records and selection in controlled state and reject or show application-specific detail; kbui does not infer partial outcomes. Do not automatically retry mutations. Avoid changing dataset identity while an action is pending; remount with a different React key when switching collections.
164
+
165
+ ### Server ownership
166
+
167
+ Supply controlled state, `mode="server"`, the current response’s rows and its filtered `totalItems`. Apply emitted query, facets, sort and page to your request. Keep `loading` true while rows belong to a previous request. Cancel or discard stale responses, reconcile out-of-range pages after deletions, and expose fetch failures with `error` and a retry action through `toolbar`. Map column sort keys to an explicit server allowlist. Validate IDs and permissions again at mutation time, including off-page records.
168
+
169
+ Offset pagination requires a known total. Cursor paging, unknown totals, all-matching remote selection, virtualization, column resizing, drag ordering and spreadsheet grid navigation are outside this API. Use bounded server pages for large remote datasets; client mode processes the entire supplied array and has no benchmark-backed dataset-size guarantee.
170
+
171
+ ### Styling and accessibility
172
+
173
+ Override `--kb-collection-gap`, `--kb-collection-cell-padding`, `--kb-collection-radius`, `--kb-collection-border`, `--kb-collection-header-background`, and `--kb-collection-selected-background`, and `--kb-collection-table-min-width` (36rem) on a subtree. Partial CSS imports also need Button, Checkbox, Input and Select styles. CSS provides layout and visual states; a non-React renderer owns every state transition and ARIA relationship.
174
+
175
+ Filters and page-size controls use the shared Select theme, keyboard navigation, option indicators and focus recovery. Labels sit above fields; filter fields wrap and pagination buttons stay grouped, with long page counts wrapping within the available width.
176
+
177
+ Native table semantics, `aria-sort`, mixed checkboxes, named pagination, polite statuses and failure alerts expose workflow state. Tab moves between controls; Space toggles checkboxes; Enter/Space activate buttons. The named table viewport is keyboard focusable for horizontal scrolling. Controls wrap on narrow screens; tabular content can scroll independently. Selection remains explicit in checkbox state in forced colors.
178
+
179
+ Verify the full search, sort, select-across-pages, submit, failure and retry journey with keyboard and a supported screen reader. Check 320 CSS pixels, enlarged text, both themes and forced colors. Automated stories cover their fixtures; manual screen-reader announcement and complete-page WCAG conformance remain unverified. Follow the [shared accessibility verification procedure](../../../../docs/guides/accessibility.md).
180
+
181
+ ## Related components
182
+
183
+ Use [Grid](Grid.md) for static card layout, [Checkbox](Checkbox.md) for independent choices, and [MultiSelect](MultiSelect.md) for choosing values rather than operating on records. Use [Dialog](Dialog.md) for application-owned confirmation and [Toast](Toast.md) for mutation outcomes outside the collection.
@@ -0,0 +1,44 @@
1
+ # CollectionList
2
+
3
+ Group records in a native list when people scan names, summaries, status and actions. Use Table when column-by-column comparison is the main task.
4
+
5
+ [All components](../../README.md#components) · [Usage](#usage) · [Examples](#more-examples) · [Reference](#reference) · [Live examples and accessibility](https://kbui.kcb.pro/storybook/?path=/docs/components-collectionlist--docs)
6
+
7
+ ## Usage
8
+
9
+ ```tsx
10
+ import "@kb.ui/react/styles.css";
11
+ import { CollectionList, CollectionRow, Link, Status } from "@kb.ui/react";
12
+
13
+ export function Example() {
14
+ return (
15
+ <CollectionList aria-label="Projects">
16
+ <CollectionRow title={<Link href="/projects/portal">Customer portal</Link>} description="Owned by Ada Chen" metadata={<Status tone="success">Ready for review</Status>} />
17
+ <CollectionRow title={<Link href="/projects/search">Search improvements</Link>} description="Owned by Lin Patel" metadata={<Status tone="warning">Needs changes</Status>} />
18
+ </CollectionList>
19
+ );
20
+ }
21
+ ```
22
+
23
+ ## More examples
24
+
25
+ - [Ordered sequence](https://kbui.kcb.pro/storybook/?path=/story/components-collectionlist--ordered): Native `ol` markers, alphabetic `type` and `start`.
26
+ - [Filtering](https://kbui.kcb.pro/storybook/?path=/story/components-collectionlist--filterable): App-owned search, result announcement, empty recovery and stable search focus.
27
+
28
+ ## Reference
29
+
30
+ | Prop | Default | Behavior |
31
+ |---|---|---|
32
+ | `as` | `ul` | `ul` for records without a required order; `ol` for an ordered sequence |
33
+ | `density` | `comfortable` | `comfortable` or `dense`; affects immediate CollectionRow children |
34
+ | `dividers` | true | Rules between immediate CollectionRow children |
35
+
36
+ The root accepts native attributes, `className`, `style` and the correct list ref. With `as="ol"`, native `start`, `reversed`, `type` and numbering behavior remain available. Public types are `CollectionListElement` and generic `CollectionListProps`. A default `role="list"` preserves list semantics where list-style removal affects accessibility exposure; the native element still supplies the structure. See the [W3C list styling guidance](https://design-system.w3.org/styles/lists.html#considerations) for marker removal and list semantics. This is an ordinary list with normal Tab navigation, without listbox selection or arrow-key item navigation.
37
+
38
+ Use CollectionRow or native `li` children. A non-row `li` retains native semantics but does not receive row slot styling. If a record has leading content or an aside, the list reserves matching columns across its immediate rows; missing optional content does not shift record titles. Metadata and actions stack beneath content below 40rem of available width. Nested lists keep their own sizing and density. Ordered markers remain visible in both directions.
39
+
40
+ Your app owns stable keys, data ordering, filtering, selection policy, pagination and requests. Keep filters mounted while results update so drafts and keyboard focus survive. Put useful empty/error guidance and a named retry action outside an empty list. Use `aria-busy` for pending content and a concise status message outside the busy subtree. Pagination or virtualization is app-owned; CollectionList renders the children you provide.
41
+
42
+ ## Related components
43
+
44
+ [CollectionRow](CollectionRow.md) supplies record slots; [DescriptionList](DescriptionList.md) presents individual record facts; [Table](Table.md) supports aligned column comparison. See [Structured data](../../../../docs/guides/structured-data.md) for composition guidance.
@@ -0,0 +1,62 @@
1
+ # CollectionRow
2
+
3
+ Make one record easy to scan inside CollectionList. Compose its primary name, supporting detail, optional selection, metadata and independent actions.
4
+
5
+ [All components](../../README.md#components) · [Usage](#usage) · [Examples](#more-examples) · [Reference](#reference) · [Live examples and accessibility](https://kbui.kcb.pro/storybook/?path=/docs/components-collectionrow--docs)
6
+
7
+ ## Usage
8
+
9
+ ```tsx
10
+ import "@kb.ui/react/styles.css";
11
+ import { Button, Checkbox, CollectionList, CollectionRow, Link, Status } from "@kb.ui/react";
12
+ import { useState } from "react";
13
+
14
+ export function Example() {
15
+ const [selected, setSelected] = useState(false);
16
+ const [message, setMessage] = useState("No action taken");
17
+ return (
18
+ <>
19
+ <p role="status">{message}</p>
20
+ <CollectionList aria-label="Projects">
21
+ <CollectionRow
22
+ title={<Link href="/projects/portal">Customer portal</Link>}
23
+ description="Owned by Ada Chen"
24
+ leading={<Checkbox label="Select Customer portal" visuallyHidden checked={selected} onCheckedChange={setSelected} />}
25
+ metadata={<Status tone="success">Ready for review</Status>}
26
+ actions={<Button variant="secondary" onClick={() => setMessage("Editing Customer portal")}>Edit portal</Button>}
27
+ selected={selected}
28
+ />
29
+ </CollectionList>
30
+ </>
31
+ );
32
+ }
33
+ ```
34
+
35
+ ## More examples
36
+
37
+ - [Selection and actions](https://kbui.kcb.pro/storybook/?path=/story/components-collectionrow--selection-and-actions): Checkbox, primary navigation, independent Buttons, Menu focus return and disabled actions.
38
+ - [Long content](https://kbui.kcb.pro/storybook/?path=/story/components-collectionrow--long-content): Wrapping identifiers, metadata and action text.
39
+
40
+ ## Reference
41
+
42
+ | Prop | Default | Behavior |
43
+ |---|---|---|
44
+ | `title` | required | Record name as a ReactNode; compose a Link or correctly ranked Heading when appropriate |
45
+ | `description` | unset | Supporting ReactNode, including DescriptionList or zero values |
46
+ | `leading` | unset | Avatar, decorative icon or a separately named selection control |
47
+ | `metadata` | unset | Status, formatted number, timestamp or other concise ReactNode |
48
+ | `actions` | unset | Named Buttons, links or Menu composition; controls keep their own semantics |
49
+ | `selected` | false | Visual state; a selection control owns accessible checked state |
50
+ | `classNames` | unset | Extra classes for `layout`, `leading`, `content`, `title`, `description`, `aside`, `metadata` and `actions` |
51
+
52
+ CollectionRow renders native `li`; place it inside CollectionList or another native list. Optional null, boolean and empty-string content omits its slot; numeric zero remains visible. Native list-item attributes, `className`, `style` and ref pass through. `CollectionRowProps` replaces the native title tooltip attribute with the required record title; use a Tooltip composition for contextual help. CollectionRow has no whole-row click handler, navigation destination or automatic focus stop. A link in title handles navigation; an action Button handles its own workflow. Independent actions do not toggle selection implicitly.
53
+
54
+ Keep a persistent, record-specific name on selection controls and actions. Hide decorative icons yourself, and supply meaningful alternative text for informative images. When content can be selected, keep `selected` consistent with Checkbox checked state; use stable IDs when filtering or sorting. Disable actual controls for unavailable operations rather than treating the whole row as disabled. A selected row is an ordinary list item, so CollectionRow does not emit grid/listbox `aria-selected` semantics.
55
+
56
+ CollectionList aligns optional leading and aside columns across rows. The defaults reserve 3rem for leading content and 12rem for an aside; override `--kb-collectionrow-leading-width` and `--kb-collectionrow-aside-width` for your content. Set padding and gap with component tokens. Below 40rem of parent list width, metadata and actions stack beneath content in DOM order. Long labels and composed Status text wrap, including chips with decorative icons. Action Buttons retain size-based minimum heights while expanding for text. Dense list spacing does not shrink control targets. Standalone native lists do not provide CollectionList's shared responsive alignment; use the paired components for this behavior.
57
+
58
+ Your app owns loading, error text, formatting, focus after a removed record, and selection scope. Avoid nesting interactive controls inside the title link or making the whole list item act as another button. Verify keyboard order, responsive wrapping and focus in your actual composition; automated stories do not establish screen-reader interoperability or complete-page accessibility.
59
+
60
+ ## Related components
61
+
62
+ Use [CollectionList](CollectionList.md) for a native record list, [DescriptionList](DescriptionList.md) for key/value details, [Table](Table.md) for comparison across columns, and [Menu](Menu.md) for overflow actions. See [Structured data](../../../../docs/guides/structured-data.md) for the shared decision guide.
@@ -0,0 +1,88 @@
1
+ # Combobox
2
+
3
+ Use Combobox to filter a longer option list before choosing one or multiple values. Each option has a string `value`, a visible `label`, and an optional `disabled` flag. Single selection uses `string | null`; enable `multiple` to use a `readonly string[]` value and a `(value: string[]) => void` callback. Pass `null` or `[]` for the corresponding empty selection.
4
+
5
+ Multiple mode delegates to [MultiSelect](MultiSelect.md), including removable chips, keyboard navigation, clear-all, form submission, and disabled/read-only states. Its popup stays open after selections and deselections, retaining the current filter. Press Escape, click outside, or use the toggle to dismiss it. It uses `.kb-multiselect` CSS anatomy and `ClassNames<MultiSelectSlot>` slots, including `chips`, `chip`, `chipLabel`, and `chipRemove`. Single mode retains `.kb-combobox` anatomy. `ComboboxProps` remains the extensible single-selection interface, so existing wrappers can keep extending it. `ComboboxSingleProps` aliases that interface; use `ComboboxMultipleProps` for a multiple-selection wrapper or React's `ComponentProps<typeof Combobox>` for the complete discriminated union.
6
+
7
+ [All components](../../README.md#components) · [Usage](#usage) · [Examples](#more-examples) · [Reference](#reference) · [Live examples and accessibility](https://kbui.kcb.pro/storybook/?path=/docs/components-combobox--docs)
8
+
9
+ ## Usage
10
+
11
+ ```tsx
12
+ import "@kb.ui/react/styles.css";
13
+ import { useState } from "react";
14
+ import { Combobox } from "@kb.ui/react";
15
+
16
+ const regions = [
17
+ { value: "us-east-1", label: "US East (N. Virginia)" },
18
+ { value: "eu-central-1", label: "EU Central (Frankfurt)" },
19
+ ];
20
+
21
+ export function RegionCombobox() {
22
+ const [region, setRegion] = useState<string | null>("us-east-1");
23
+ return (
24
+ <Combobox
25
+ label="Deployment region"
26
+ options={regions}
27
+ value={region}
28
+ onValueChange={setRegion}
29
+ placeholder="Search regions..."
30
+ />
31
+ );
32
+ }
33
+ ```
34
+
35
+ ### Multiple selections
36
+
37
+ ```tsx
38
+ import "@kb.ui/react/styles.css";
39
+ import { useState } from "react";
40
+ import { Combobox } from "@kb.ui/react";
41
+
42
+ export function DeploymentRegions() {
43
+ const [regions, setRegions] = useState<string[]>([]);
44
+ return (
45
+ <Combobox
46
+ multiple
47
+ label="Deployment regions"
48
+ options={[
49
+ { value: "us-east-1", label: "US East (N. Virginia)" },
50
+ { value: "eu-central-1", label: "EU Central (Frankfurt)" },
51
+ ]}
52
+ value={regions}
53
+ onValueChange={setRegions}
54
+ placeholder="Search regions..."
55
+ />
56
+ );
57
+ }
58
+ ```
59
+
60
+ ## More examples
61
+
62
+ - [Multiple selections](https://kbui.kcb.pro/storybook/?path=/story/components-combobox--multiple): Filter once, choose several options, then remove chips individually.
63
+ - [Retained selection](https://kbui.kcb.pro/storybook/?path=/story/components-combobox--preselected): Start with a known option.
64
+ - [No matching options](https://kbui.kcb.pro/storybook/?path=/story/components-combobox--empty): Inspect the empty state when filtering finds nothing.
65
+ - [Validation feedback](https://kbui.kcb.pro/storybook/?path=/story/components-combobox--invalid): Associate a correction with the field.
66
+
67
+ ## Reference
68
+
69
+ | Prop | Type | Default | Notes |
70
+ |---|---|---|---|
71
+ | `label` | `string` | required | Visible field label and accessible combobox name |
72
+ | `options` | `readonly ComboboxOption[]` | required | Selectable options filtered by label |
73
+ | `multiple` | `boolean` | `false` | Enable multiple selections; determines value and callback types |
74
+ | `value` | `string \| null` or `readonly string[]` | required | Single selection or multiple selected values |
75
+ | `onValueChange` | `(value: string \| null) => void` or `(value: string[]) => void` | required | Callback matches selection mode |
76
+ | `placeholder` | `string` | unset | Input placeholder shown when empty |
77
+ | `emptyMessage` | `ReactNode` | `"No matching options"` | Polite status text when no options match |
78
+ | `size` | `"sm" \| "md" \| "lg"` | `"md"` | Control height and text scale |
79
+ | `description` / `error` | `ReactNode` | unset | Associated help and invalid-state text |
80
+ | `disabled` / `readOnly` | `boolean` | `false` | Prevent edits; disabled also blocks form submission |
81
+ | `clearLabel` / `toggleLabel` | `string` | derived from `label` | Accessible names for the clear and toggle buttons |
82
+ | `container` | `Combobox.Portal.Props["container"]` | field root | Portal host; default retains nested theme |
83
+ | `classNames` | `ClassNames<ComboboxSlot>` or `ClassNames<MultiSelectSlot>` | unset | Slots match selection mode |
84
+ | `removeLabel` | `(option: ComboboxOption) => string` | `"Remove {option.label}"` | Multiple mode only; accessible chip removal name |
85
+
86
+ ## Related components
87
+
88
+ Use [Select](Select.md) for a dropdown without text filtering, [RadioGroup](RadioGroup.md) for visible options, and [MultiSelect](MultiSelect.md) for several searchable choices.
@@ -0,0 +1,41 @@
1
+ # Container
2
+
3
+ Centered page boundary with responsive inline gutters. Native props and matching ref pass through. See [Layout composition](https://github.com/KhallilB/kbui/blob/main/docs/guides/layout.md) for rules, CSS recipes and token overrides.
4
+
5
+ [All components](../../README.md#components) · [Usage](#usage) · [Examples](#more-examples) · [Reference](#reference) · [Live examples and accessibility](https://kbui.kcb.pro/storybook/?path=/docs/layout-container--docs)
6
+
7
+ ## Usage
8
+
9
+ ```tsx
10
+ import "@kb.ui/react/styles.css";
11
+ import { Container } from "@kb.ui/react";
12
+
13
+ export function Example() {
14
+ return (
15
+ <Container as="main">
16
+ <h1>Projects</h1>
17
+ <p>Your current work.</p>
18
+ </Container>
19
+ );
20
+ }
21
+ ```
22
+
23
+ ## More examples
24
+
25
+ - [A composed page](https://kbui.kcb.pro/storybook/?path=/story/layout-container--page): Combine a page boundary, content, and actions.
26
+ - [Narrow viewports](https://kbui.kcb.pro/storybook/?path=/story/layout-container--responsive): Compare responsive gutters.
27
+ - [Width choices](https://kbui.kcb.pro/storybook/?path=/story/layout-container--sizes): Compare available page widths.
28
+
29
+ ## Reference
30
+
31
+ | Prop | Values | Default |
32
+ | -------- | ---------------------------------------------------------- | ------- |
33
+ | `as` | `div` `main` `section` `article` `aside` `header` `footer` | `div` |
34
+ | `size` | `sm` (40rem) `md` (64rem) `lg` (80rem) `full` (unbounded) | `lg` |
35
+ | `gutter` | Shared 1–2rem inline padding; false removes it | `true` |
36
+
37
+ Non-default generic native elements require an explicit `as`; native props and refs follow that element. Explicit `gutter` owns its data attribute even when caller attributes conflict. Widths include gutters. Use one page boundary; nested containers should disable gutters when the parent owns padding.
38
+
39
+ ## Related components
40
+
41
+ Use [Inline](Inline.md) for a row, [Grid](Grid.md) for a collection, and [Split](Split.md) for two responsive columns.
@@ -0,0 +1,93 @@
1
+ # CopyableText
2
+
3
+ Copy identifiers, commands and references with explicit feedback. Values remain selectable, including when clipboard permission is denied.
4
+
5
+ [All components](../../README.md#components) · [Usage](#usage) · [Reference](#reference) · [Live examples and accessibility](https://kbui.kcb.pro/storybook/?path=/docs/components-copyabletext--docs)
6
+
7
+ ## Usage
8
+
9
+ ```tsx
10
+ import "@kb.ui/react/styles.css";
11
+ import { CopyableText } from "@kb.ui/react";
12
+
13
+ export function Example() {
14
+ return (
15
+ <CopyableText
16
+ label="Request ID"
17
+ value="req_01JCP6NF8B4M2WQ9K7X3"
18
+ copyLabel="Copy request ID"
19
+ />
20
+ );
21
+ }
22
+ ```
23
+
24
+ `value` is the clipboard source. Case, whitespace, Unicode and trailing newlines are preserved. `children` can format the collapsed display independently; expanding or a copy failure reveals the exact value for inspection and manual copying. Do not use this component to conceal secrets.
25
+
26
+ The native button retains focus while a write is pending. Repeated activation during that write does nothing. Success appears only after the Clipboard API resolves; its callback receives the copied value. If value changes, the component is disabled, or it unmounts, outstanding writes cannot update feedback or fire callbacks. A write already sent to the operating system cannot be cancelled.
27
+
28
+ A visible failure message remains until retry or content change. Users can select the full value and copy manually. Clipboard writes require a secure context such as HTTPS or localhost; browser permission, user activation, iframe policy or host restrictions can still reject them. No legacy clipboard fallback is attempted.
29
+
30
+ With `truncate`, a single-line preview has an explicit `Show full value` button. Activating it wraps the complete raw value across the field width; `Show less` restores the compact preview. The action works with touch, Enter and Space, retains focus, and exposes `aria-expanded` and `aria-controls`. It stays available when copying is disabled. A changed value resets the disclosure. Failure automatically expands it; users can collapse it again deliberately. CSS truncation preserves full text in the accessibility tree; disclosure controls visual wrapping. No hover-only tooltip is required.
31
+
32
+ Set `tone` to color the value and softly tint field appearance. Neutral is the default; accent, info, success, warning and danger reuse semantic theme colors. Tone is emphasis, not clipboard state or an announcement. Keep meaning in the label and value, including forced-colors mode. For a custom neutral color, override `--kb-copyabletext-value-color`; use `classNames.value` for richer display styling.
33
+
34
+ Set `highlight` to a string or array of literal, case-sensitive terms. Matches use semantic `mark` elements with bold emphasis; the longest match wins when terms overlap at the same position. Matching never interprets HTML or regular expressions and never changes copied content.
35
+
36
+ Automatic `format="auto"` recognizes HTTP(S) and `www.` URLs, qualified ASCII email addresses, backtick-delimited inline code, fenced code, valid JSON objects/arrays, and conservative JavaScript/TypeScript, CSS, HTML and common package-manager/Git/curl command patterns. Code receives the existing syntax colors and suppresses navigation inside source. Delimiters, whitespace and clipboard bytes remain literal.
37
+
38
+ Use `format="text"` for identifiers or prose that resemble code, or `format="code"` with `language` for explicit source. A language hint forces code in automatic mode. Detection is intentionally conservative; it does not validate email deliverability or infer every programming language. Unknown fenced languages render as plain code. For large listings, prefer CodeBlock.
39
+
40
+ Detected links reuse Link styling and Link.Provider. Emails use mailto destinations with an encoded address; web links remain underlined native anchors. Sentence punctuation and unmatched closing brackets stay outside anchors. Set `linkify={false}` to disable detected links, or `linkify` with `format="text"` to opt back into links without code detection. Authored children retain caller-owned Link, strong, em or Code composition; automatic formatting applies to displayed raw source.
41
+
42
+ In truncated previews, detected links become interactive only after expansion. This keeps clipped links out of Tab order. After expanding, Shift+Tab reaches links above the disclosure; Tab continues to Copy. Copying remains a separate action. Errors expand the same formatted raw source for manual selection. `mono={false}` gives prose the proportional font and body line spacing.
43
+
44
+ ## More examples
45
+
46
+ - [Automatic recognition](https://kbui.kcb.pro/storybook/?path=/story/components-copyabletext--automatic): URLs, emails, inline commands and source code.
47
+ - [Text and links](https://kbui.kcb.pro/storybook/?path=/story/components-copyabletext--rich-content): prose with literal highlights and native web links.
48
+ - [References](https://kbui.kcb.pro/storybook/?path=/story/components-copyabletext--references): identifiers, URLs and invitation text.
49
+ - [Inline](https://kbui.kcb.pro/storybook/?path=/story/components-copyabletext--inline): compact reference beside prose.
50
+ - [Long values](https://kbui.kcb.pro/storybook/?path=/story/components-copyabletext--long-value): expand and collapse a full-width wrapped value.
51
+ - [Colors](https://kbui.kcb.pro/storybook/?path=/story/components-copyabletext--colors): six value tones with matching field tints.
52
+ - [Copy feedback](https://kbui.kcb.pro/storybook/?path=/story/components-copyabletext--copy-feedback): pending, focus retention and success.
53
+ - [Copy failure](https://kbui.kcb.pro/storybook/?path=/story/components-copyabletext--copy-failure): persistent recovery instructions.
54
+ - [Localized](https://kbui.kcb.pro/storybook/?path=/story/components-copyabletext--localized): translated button and announcement text.
55
+
56
+ ## Reference
57
+
58
+ | Prop | Type | Default | Notes |
59
+ | --- | --- | --- | --- |
60
+ | `value` | `string` | required | Exact clipboard contents; empty string disables action |
61
+ | `children` | `ReactNode` | `value` | Optional display; exact value is shown on failure |
62
+ | `label` | `string` | unset | Visible context above value; does not name the button |
63
+ | `appearance` | `"field" \| "plain"` | `"field"` | Bordered field or inline treatment |
64
+ | `size` | `"sm" \| "md"` | `"md"` | Density; default copy target follows button height token |
65
+ | `mono` | `boolean` | `true` | Monospace value display |
66
+ | `tone` | `CopyableTextTone` | `"neutral"` | `neutral`, `accent`, `info`, `success`, `warning` or `danger`; independent of copy state |
67
+ | `truncate` | `boolean` | `false` | Single-line preview with explicit full-value disclosure |
68
+ | `expandLabel` | `string` | `"Show full value"` | Disclosure text when collapsed |
69
+ | `collapseLabel` | `string` | `"Show less"` | Disclosure text when expanded |
70
+ | `disabled` | `boolean` | `false` | Disables copying, retains selectable value |
71
+ | `copyLabel` | `string` | `"Copy"` | Visible action and accessible name; use specific purpose |
72
+ | `copyingLabel` | `string` | `"Copying…"` | Pending label and polite announcement |
73
+ | `copiedLabel` | `string` | `"Copied"` | Success label |
74
+ | `copyErrorLabel` | `string` | `"Retry copy"` | Failure label; action retries |
75
+ | `successMessage` | `string` | `"Copied to clipboard"` | Polite success announcement |
76
+ | `errorMessage` | `string` | `"Copy failed. Select and copy the text manually."` | Visible polite error and recovery instructions |
77
+ | `feedbackDuration` | `number` | `2000` | Success reset in milliseconds; zero retains success, invalid or negative uses default |
78
+ | `onCopy` | `(value: string) => void` | unset | Current write succeeded; replaces native clipboard event prop |
79
+ | `onCopyError` | `(error: unknown) => void` | unset | Current write failed, including unavailable API |
80
+ | `highlight` | `string \| readonly string[]` | unset | Literal case-sensitive terms; empty terms ignored, longest overlapping match wins |
81
+ | `format` | `CopyableTextFormat` | `"auto"` | `auto`, `text`, or `code`; conservative recognition with explicit overrides |
82
+ | `language` | `CodeLanguage` | unset | Code hint; forces code in auto mode; text mode remains literal |
83
+ | `linkify` | `boolean` | automatic | Override detected web/email links; false disables links; code ranges never become links |
84
+ | `classNames` | `Partial<Record<CopyableTextPart, string>>` | unset | `label`, `value`, `actions`, `toggle`, `copy`, `feedback`, `link`, `highlight`, `code` slots |
85
+ | `className`, `ref`, other props | Native `span` props | unset | Root element; native `onCopy` replaced by success callback |
86
+
87
+ The root exposes `data-copy-state="idle|pending|copied|failed"`, appearance, size, tone, mono, expandable, expanded, truncate and disabled attributes. Pending sets `aria-disabled` on the button while preserving focus. The status region exists before interaction and is empty initially. Failure is both visible and announced; success has a visible button label and polite announcement.
88
+
89
+ Use `--kb-copyabletext-highlight-background` for marked text and Link tokens for navigation styling. Standalone CSS users also load `link.css` and `code.css` for detected links and syntax styling. Use `--kb-copyabletext-*` tokens for gap, padding, radius, border, background, neutral value color, target size and minimum copy width. `className` styles the root; `classNames` styles individual parts. Verify contrast and target sizes after overrides. Automated tests cover component behavior, browser keyboard activation and accessibility scans; screen-reader announcement quality remains a human verification task.
90
+
91
+ ## Related components
92
+
93
+ Use [CodeBlock](CodeBlock.md) for highlighted source and multiline commands, [Code](Code.md) for inline literals, and [Link](Link.md) for navigation. [Copyable content guidance](https://github.com/KhallilB/kbui/blob/main/docs/guides/copyable-content.md) covers shared recovery and localization.
@@ -0,0 +1,61 @@
1
+ # DescriptionList
2
+
3
+ Present named facts about one record with native description-list relationships. Use Table when readers need to compare the same fields across records.
4
+
5
+ [All components](../../README.md#components) · [Usage](#usage) · [Examples](#more-examples) · [Reference](#reference) · [Live examples and accessibility](https://kbui.kcb.pro/storybook/?path=/docs/components-descriptionlist--docs)
6
+
7
+ ## Usage
8
+
9
+ ```tsx
10
+ import "@kb.ui/react/styles.css";
11
+ import { DescriptionList, Link, Status } from "@kb.ui/react";
12
+
13
+ export function Example() {
14
+ return (
15
+ <DescriptionList aria-label="Project details">
16
+ <DescriptionList.Item>
17
+ <DescriptionList.Term>Owner</DescriptionList.Term>
18
+ <DescriptionList.Detail><Link href="/people/ada">Ada Chen</Link></DescriptionList.Detail>
19
+ </DescriptionList.Item>
20
+ <DescriptionList.Item>
21
+ <DescriptionList.Term>Status</DescriptionList.Term>
22
+ <DescriptionList.Detail><Status tone="success">Ready for review</Status></DescriptionList.Detail>
23
+ </DescriptionList.Item>
24
+ <DescriptionList.Item>
25
+ <DescriptionList.Term>Open tasks</DescriptionList.Term>
26
+ <DescriptionList.Detail>{0}</DescriptionList.Detail>
27
+ </DescriptionList.Item>
28
+ </DescriptionList>
29
+ );
30
+ }
31
+ ```
32
+
33
+ ## More examples
34
+
35
+ - [Responsive](https://kbui.kcb.pro/storybook/?path=/story/components-descriptionlist--responsive): Stacks according to its parent width.
36
+ - [Nested details](https://kbui.kcb.pro/storybook/?path=/story/components-descriptionlist--nested): Independent density and layout inside another detail value.
37
+
38
+ ## Reference
39
+
40
+ | Part | Native element | Purpose |
41
+ |---|---|---|
42
+ | `DescriptionList` | `dl` | Root description list; native naming attributes, children, styles and ref pass through |
43
+ | `DescriptionList.Item` | `div` | Groups one Term with one or more Detail elements |
44
+ | `DescriptionList.Term` | `dt` | Meaningful label in reading order before its values |
45
+ | `DescriptionList.Detail` | `dd` | Text, numbers, links, status or richer content; repeated Details support multiple values |
46
+
47
+ | Prop | Default | Behavior |
48
+ |---|---|---|
49
+ | `layout` | `responsive` | `responsive` uses label/value columns, stacking below 32rem of available width; `horizontal` keeps columns; `stacked` always puts values below labels |
50
+ | `density` | `comfortable` | `comfortable` or `dense`; changes immediate Item spacing |
51
+ | `dividers` | true | Rules between immediate groups |
52
+
53
+ Every part accepts its native attributes, `className`, `style` and ref. Types are `DescriptionListProps`, `DescriptionListItemProps`, `DescriptionListTermProps` and `DescriptionListDetailProps`. Use a real `dt` before its `dd` values; the description list does not become a table or listbox. Group alternate names inside one Term when they share values. Native IDs and `aria-labelledby` remain available for richer relationships.
54
+
55
+ Values align to the same column across Items. Long labels, identifiers and composed Status text wrap, including chips with decorative icons; native links and controls retain their keyboard behavior. Use a deliberate stacked layout for narrow details, or override `--kb-descriptionlist-term-width` when the default 12rem label cap does not fit your labels. Other tokens control column/row gaps and comfortable/dense block padding. Nested lists keep their own layout and density.
56
+
57
+ Keep zero values visible. Write a meaningful unavailable value such as Not scheduled rather than silently omitting a required fact. Your app owns formatting, units, timezone, localization, loading, errors and announcements. Do not use term text as an input label; controls still need their own labels and relationships. A description list supplies native relationships, not interactive field validation.
58
+
59
+ ## Related components
60
+
61
+ Use [Table](Table.md) for comparing fields across records, [CollectionRow](CollectionRow.md) for summarizing records with actions, and [Status](Status.md) for an explicit state. See [Structured data](../../../../docs/guides/structured-data.md) for choosing and composing these patterns.