@nadicodeai/ui 12.1.134 → 12.1.135

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.
@@ -228,7 +228,7 @@ export declare function columnMenuLabel<TData extends RowData, TValue extends Ce
228
228
  * @instead “For a row of descriptive content paired with a single action (membership row, integration row), use `Entity` instead.” (https://vercel.com/geist/table)
229
229
  * “For a key/value metadata block on a detail page, use `Description`, not a two-column table.” (https://vercel.com/geist/table)
230
230
  * Ours: Geist's `Entity` is a `RecordList` row. A registry of entities whose columns `RecordList` cannot hold, more than three facts beside a filter bar the product owns, as Vercel's Deployments list, is this table with `rows="entities"`, which draws the same rows: headings for assistive technology only, 72 px rows, a `RecordIdentity` in the identity column. Its `Description` is `ObjectFacts`. A list on a page of its own is a `CollectionPage`. A descriptive list is `ItemGroup`.
231
- * @behaviour Every column has a `header`; a column without one does not compile. The headings are always visible, medium weight in the cells' body grey, 48 px high, with 12 px of room under the labels and then the rule, so the first row starts at the rule like every other; rows are 40 px. Each column is as wide as its definition's `size`, in pixels, and the columns without one share the rest, so a table gives a size to every column but the one that takes the spare width; a value too long for its column ends in an ellipsis, whole in its title (a record's name carries its own), unless the column `wraps`; a column marked `identifier` holds ids or paths and is cut in the middle instead. The last column trails unless it `wraps` or is `centered`; a `centered` column's heading and values sit in the middle of it. Records are divided by a 1 px rule, none after the last. The first and last columns stand in from the card's edges by the card's spacing, so the first lines up with the title. Records of several kinds stand in one table under `group` headings (Vercel's Usage page: *Delivery network*, *Functions*): a heading row that spans the columns, in ink, medium text, 36 px high with the same rule under it, one per group in the order the groups first appear, never a second table; a heading is the group's name and carries no figure of its own. A row that opens a record takes `accent` on hover, and any row on focus within and while its menu is open; a row that opens nothing never reacts to hover. A selected row takes `muted`; headers and footers never highlight and carry no fill. A row never scrolls sideways: on a card narrower than its columns (the sized ones and 160 px for each without a size) the row keeps its name and its `primaryColumns`, and every other column leaves it, its value joining the one muted line under the name, joined by a middle dot and ending in an ellipsis (a `Tag` reads as its text there, and a `wraps` column's prose wraps the line whole), or `dropped` when its `compact` says so, or beside an entity row's name, which ends in an ellipsis before it, when it says `name`, as Vercel's lists read on a phone: no labels, no second line; an `optional` column's empty value is left out of the line. A table whose every column is in `primaryColumns`, a settings matrix, lays out by its content there instead, so the name wraps beside the columns. A document's table, such as a legal page's, is read whole instead: its columns take the `wraps` option, so their words wrap and the row grows, and it keeps its header row; in the compact layout every cell but the identity wraps between words. A bounded page's paging stands in the card's footer strip at its trailing edge, opposite `footer`, which stands at the leading edge: its `status` ("Page 2 of 7") before Previous and Next, which are the product's router links (`previousLink`, `nextLink`) when each page is its own address, and buttons otherwise; the one that leads nowhere stays, disabled. A note about the whole table is `footerNote`, in a strip of its own under that one: one muted line, an optional *Learn more* after it at the left and one secondary action at the right (Vercel's deployments list ends "Deployment retention is enabled. Learn more" with *View recently deleted*); it is the one place a table says a sentence. Colour is neutral: headings and cells in the body grey, no hue, except a status badge or a tone the cell itself carries.
231
+ * @behaviour Every column has a `header`; a column without one does not compile. The headings are always visible, medium weight in the cells' body grey, 48 px high, with 12 px of room under the labels and then the rule, so the first row starts at the rule like every other; rows are 40 px. Each column is as wide as its definition's `size`, in pixels, and the columns without one share the rest, so a table gives a size to every column but the one that takes the spare width; a value too long for its column ends in an ellipsis, whole in its title (a record's name carries its own), unless the column `wraps`; a column marked `identifier` holds ids or paths and is cut in the middle instead. The last column trails unless it `wraps` or is `centered`; a `centered` column's heading and values sit in the middle of it. Records are divided by a 1 px rule, none after the last. The first and last columns stand in from the card's edges by the card's spacing, so the first lines up with the title. Records of several kinds stand in one table under `group` headings (Vercel's Usage page: *Delivery network*, *Functions*): a heading row that spans the columns, in ink, medium text, 36 px high with the same rule under it, one per group in the order the groups first appear, never a second table; a heading is the group's name and carries no figure of its own. A row that opens a record takes `accent` on hover, and any row on focus within and while its menu is open; a row that opens nothing never reacts to hover. A selected row takes `muted`; headers and footers never highlight and carry no fill. A row never scrolls sideways: on a card narrower than its columns (the sized ones and 160 px for each without a size) the row keeps its name and its `primaryColumns`, and every other column leaves it, its value joining the one muted line under the name, joined by a middle dot (a `Tag` reads as its text there); entity facts wrap below the name so every value stays readable, while other rows end in an ellipsis unless a column `wraps`, or `dropped` when its `compact` says so, or beside an entity row's name, which ends in an ellipsis before it, when it says `name`, as Vercel's lists read on a phone: no labels, no second line; an `optional` column's empty value is left out of the line. A table whose every column is in `primaryColumns`, a settings matrix, lays out by its content there instead, so the name wraps beside the columns. A document's table, such as a legal page's, is read whole instead: its columns take the `wraps` option, so their words wrap and the row grows, and it keeps its header row; in the compact layout every cell but the identity wraps between words. A bounded page's paging stands in the card's footer strip at its trailing edge, opposite `footer`, which stands at the leading edge: its `status` ("Page 2 of 7") before Previous and Next, which are the product's router links (`previousLink`, `nextLink`) when each page is its own address, and buttons otherwise; the one that leads nowhere stays, disabled. A note about the whole table is `footerNote`, in a strip of its own under that one: one muted line, an optional *Learn more* after it at the left and one secondary action at the right (Vercel's deployments list ends "Deployment retention is enabled. Learn more" with *View recently deleted*); it is the one place a table says a sentence. Colour is neutral: headings and cells in the body grey, no hue, except a status badge or a tone the cell itself carries.
232
232
  * “When the underlying list is empty (filter cleared, never created), render `Empty State` outside the table rather than an empty `<Table.Body>`.” (https://vercel.com/geist/table)
233
233
  * “After an async filter change, wrap the region in `aria-live="polite"` so screen readers announce the new state.” (https://vercel.com/geist/empty-state)
234
234
  * Ours: empty sits outside the body, in a polite live region that stays in place so a filter that empties the table is announced.
@@ -183,7 +183,7 @@ function DefaultToolbar({ actions }) {
183
183
  * @instead “For a row of descriptive content paired with a single action (membership row, integration row), use `Entity` instead.” (https://vercel.com/geist/table)
184
184
  * “For a key/value metadata block on a detail page, use `Description`, not a two-column table.” (https://vercel.com/geist/table)
185
185
  * Ours: Geist's `Entity` is a `RecordList` row. A registry of entities whose columns `RecordList` cannot hold, more than three facts beside a filter bar the product owns, as Vercel's Deployments list, is this table with `rows="entities"`, which draws the same rows: headings for assistive technology only, 72 px rows, a `RecordIdentity` in the identity column. Its `Description` is `ObjectFacts`. A list on a page of its own is a `CollectionPage`. A descriptive list is `ItemGroup`.
186
- * @behaviour Every column has a `header`; a column without one does not compile. The headings are always visible, medium weight in the cells' body grey, 48 px high, with 12 px of room under the labels and then the rule, so the first row starts at the rule like every other; rows are 40 px. Each column is as wide as its definition's `size`, in pixels, and the columns without one share the rest, so a table gives a size to every column but the one that takes the spare width; a value too long for its column ends in an ellipsis, whole in its title (a record's name carries its own), unless the column `wraps`; a column marked `identifier` holds ids or paths and is cut in the middle instead. The last column trails unless it `wraps` or is `centered`; a `centered` column's heading and values sit in the middle of it. Records are divided by a 1 px rule, none after the last. The first and last columns stand in from the card's edges by the card's spacing, so the first lines up with the title. Records of several kinds stand in one table under `group` headings (Vercel's Usage page: *Delivery network*, *Functions*): a heading row that spans the columns, in ink, medium text, 36 px high with the same rule under it, one per group in the order the groups first appear, never a second table; a heading is the group's name and carries no figure of its own. A row that opens a record takes `accent` on hover, and any row on focus within and while its menu is open; a row that opens nothing never reacts to hover. A selected row takes `muted`; headers and footers never highlight and carry no fill. A row never scrolls sideways: on a card narrower than its columns (the sized ones and 160 px for each without a size) the row keeps its name and its `primaryColumns`, and every other column leaves it, its value joining the one muted line under the name, joined by a middle dot and ending in an ellipsis (a `Tag` reads as its text there, and a `wraps` column's prose wraps the line whole), or `dropped` when its `compact` says so, or beside an entity row's name, which ends in an ellipsis before it, when it says `name`, as Vercel's lists read on a phone: no labels, no second line; an `optional` column's empty value is left out of the line. A table whose every column is in `primaryColumns`, a settings matrix, lays out by its content there instead, so the name wraps beside the columns. A document's table, such as a legal page's, is read whole instead: its columns take the `wraps` option, so their words wrap and the row grows, and it keeps its header row; in the compact layout every cell but the identity wraps between words. A bounded page's paging stands in the card's footer strip at its trailing edge, opposite `footer`, which stands at the leading edge: its `status` ("Page 2 of 7") before Previous and Next, which are the product's router links (`previousLink`, `nextLink`) when each page is its own address, and buttons otherwise; the one that leads nowhere stays, disabled. A note about the whole table is `footerNote`, in a strip of its own under that one: one muted line, an optional *Learn more* after it at the left and one secondary action at the right (Vercel's deployments list ends "Deployment retention is enabled. Learn more" with *View recently deleted*); it is the one place a table says a sentence. Colour is neutral: headings and cells in the body grey, no hue, except a status badge or a tone the cell itself carries.
186
+ * @behaviour Every column has a `header`; a column without one does not compile. The headings are always visible, medium weight in the cells' body grey, 48 px high, with 12 px of room under the labels and then the rule, so the first row starts at the rule like every other; rows are 40 px. Each column is as wide as its definition's `size`, in pixels, and the columns without one share the rest, so a table gives a size to every column but the one that takes the spare width; a value too long for its column ends in an ellipsis, whole in its title (a record's name carries its own), unless the column `wraps`; a column marked `identifier` holds ids or paths and is cut in the middle instead. The last column trails unless it `wraps` or is `centered`; a `centered` column's heading and values sit in the middle of it. Records are divided by a 1 px rule, none after the last. The first and last columns stand in from the card's edges by the card's spacing, so the first lines up with the title. Records of several kinds stand in one table under `group` headings (Vercel's Usage page: *Delivery network*, *Functions*): a heading row that spans the columns, in ink, medium text, 36 px high with the same rule under it, one per group in the order the groups first appear, never a second table; a heading is the group's name and carries no figure of its own. A row that opens a record takes `accent` on hover, and any row on focus within and while its menu is open; a row that opens nothing never reacts to hover. A selected row takes `muted`; headers and footers never highlight and carry no fill. A row never scrolls sideways: on a card narrower than its columns (the sized ones and 160 px for each without a size) the row keeps its name and its `primaryColumns`, and every other column leaves it, its value joining the one muted line under the name, joined by a middle dot (a `Tag` reads as its text there); entity facts wrap below the name so every value stays readable, while other rows end in an ellipsis unless a column `wraps`, or `dropped` when its `compact` says so, or beside an entity row's name, which ends in an ellipsis before it, when it says `name`, as Vercel's lists read on a phone: no labels, no second line; an `optional` column's empty value is left out of the line. A table whose every column is in `primaryColumns`, a settings matrix, lays out by its content there instead, so the name wraps beside the columns. A document's table, such as a legal page's, is read whole instead: its columns take the `wraps` option, so their words wrap and the row grows, and it keeps its header row; in the compact layout every cell but the identity wraps between words. A bounded page's paging stands in the card's footer strip at its trailing edge, opposite `footer`, which stands at the leading edge: its `status` ("Page 2 of 7") before Previous and Next, which are the product's router links (`previousLink`, `nextLink`) when each page is its own address, and buttons otherwise; the one that leads nowhere stays, disabled. A note about the whole table is `footerNote`, in a strip of its own under that one: one muted line, an optional *Learn more* after it at the left and one secondary action at the right (Vercel's deployments list ends "Deployment retention is enabled. Learn more" with *View recently deleted*); it is the one place a table says a sentence. Colour is neutral: headings and cells in the body grey, no hue, except a status badge or a tone the cell itself carries.
187
187
  * “When the underlying list is empty (filter cleared, never created), render `Empty State` outside the table rather than an empty `<Table.Body>`.” (https://vercel.com/geist/table)
188
188
  * “After an async filter change, wrap the region in `aria-live="polite"` so screen readers announce the new state.” (https://vercel.com/geist/empty-state)
189
189
  * Ours: empty sits outside the body, in a polite live region that stays in place so a filter that empties the table is announced.
@@ -291,8 +291,6 @@ function RecordsTable({ kind, columns, data, getRowId, filterColumnId, filterPla
291
291
  wraps: cell.column.columnDef.meta?.wraps === true,
292
292
  besideName: entities && cell.column.columnDef.meta?.compact === "name",
293
293
  }));
294
- // An entity row keeps its record on one 72 px row on a narrow card: the facts that leave the
295
- // row join the line under the name, which `RecordIdentity` draws, instead of a row of their own.
296
294
  // The line's provider stays in the tree when the card is wide enough for the columns and only
297
295
  // its facts go, so a layout read after hydration never remounts the name a click is landing on.
298
296
  const compactLine = entities && compactLayout && detailCells.length > 0
@@ -323,8 +321,7 @@ function RecordsTable({ kind, columns, data, getRowId, filterColumnId, filterPla
323
321
  return _jsx(TableCell, { className: className }, cell.id);
324
322
  }
325
323
  const content = holdsCompactLine ? (_jsx(TableCompactLine, { ...compactLine, children: renderCell(cell) })) : (renderCell(cell));
326
- const titledWhenCut = cutByEllipsis || (holdsCompactLine && compactLine.facts.length > 0);
327
- return (_jsx(TableCell, { className: className, onFocus: titledWhenCut ? titleWhenCut : undefined, onPointerEnter: titledWhenCut ? titleWhenCut : undefined, children: isPrimaryLink
324
+ return (_jsx(TableCell, { className: className, onFocus: cutByEllipsis ? titleWhenCut : undefined, onPointerEnter: cutByEllipsis ? titleWhenCut : undefined, children: isPrimaryLink
328
325
  ? renderRowLink(primaryRowLinkProps({ href, label, wraps, content }))
329
326
  : content }, cell.id));
330
327
  }) }), rowEditor?.rowId === row.id ? (_jsx(TableRow, { "data-slot": "table-row-editor", className: "h-auto! [&>td]:bg-transparent!", children: _jsx(TableCell, { colSpan: visibleCells.length, className: "p-0 whitespace-normal", children: rowEditor.render(row.original) }) })) : null, detailRowShown ? (_jsx(TableDetailRow, { layout: compactLayout, colSpan: visibleCells.filter((cell) => !collapseClass(cell.column, primaryColumns, compactLayout)).length, facts: detailFacts() })) : null] }));
@@ -8,7 +8,7 @@ import { type IdentityMarkProps } from "./identity-mark.js";
8
8
  * @family Status
9
9
  * @when The first cell of every collection row: the mark, the name, and one muted line saying what the record is or where it comes from, so the row reads without a heading.
10
10
  * @instead An Agent in a gallery is `AgentListRow`; a model is `ModelIdentity`. A descriptive item with prose and actions is `Item`.
11
- * @behaviour The mark stands 16 px before the name. The name is semibold (600) ink and the detail a muted 14 px line, two lines with no gap between them, so the row is an entity row; `mark` is required; a record with nothing to say under its name leaves `detail` out and is the name alone, never a dash, which is a table cell's missing value and not a record's. The name holds one line and ends in an ellipsis, whole in its title, with a `hint` such as an ID read after it. The detail is one line too and ends in an ellipsis, whole in its title when it is text; in a row on a card narrower than its columns, the facts that leave the row join it after a middle dot, so the record stays the name over one line, and a status that leaves the row stands beside the name, which ends in an ellipsis before it. `wraps` only when the name is the whole record, such as a memory, and then the detail wraps too. The table owns the link.
11
+ * @behaviour The mark stands 16 px before the name. The name is semibold (600) ink and the detail a muted 14 px line, two lines with no gap between them, so the row is an entity row; `mark` is required; a record with nothing to say under its name leaves `detail` out and is the name alone, never a dash, which is a table cell's missing value and not a record's. The name holds one line and ends in an ellipsis, whole in its title, with a `hint` such as an ID read after it. The detail is one line too and ends in an ellipsis, whole in its title when it is text; in a row on a card narrower than its columns, the facts that leave the row join it after a middle dot and wrap so every value stays readable, and a status that leaves the row stands beside the name, which ends in an ellipsis before it. `wraps` only when the name is the whole record, such as a memory, and then the detail wraps too. The table owns the link.
12
12
  * @words “Lead the left column with a scannable identifier: an `Avatar` or icon, a Title Case label, then sentence-case secondary metadata (`Member since Mar 14, 2026`).” (https://vercel.com/geist/entity)
13
13
  * Ours: the name as the record calls itself, and the detail in sentence case: kind, then relation, three to six words, "Company skill · every Agent has it".
14
14
  * @access The name is the row link's text; the mark is decorative; the title carries the full name.
@@ -1 +1 @@
1
- {"version":3,"file":"record-identity.d.ts","sourceRoot":"","sources":["../../src/components/record-identity.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAIlF;;;;;;;;;;;;GAYG;AACH,iBAAS,cAAc,CAAC,EACtB,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,KAAa,GACd,EAAE;IACD,IAAI,EAAE,SAAS,CAAC;IAChB,8FAA8F;IAC9F,IAAI,EAAE,iBAAiB,CAAC;IACxB,qFAAqF;IACrF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,+BAsCA;AAED,OAAO,EAAE,cAAc,EAAE,CAAC"}
1
+ {"version":3,"file":"record-identity.d.ts","sourceRoot":"","sources":["../../src/components/record-identity.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAIlF;;;;;;;;;;;;GAYG;AACH,iBAAS,cAAc,CAAC,EACtB,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,KAAa,GACd,EAAE;IACD,IAAI,EAAE,SAAS,CAAC;IAChB,8FAA8F;IAC9F,IAAI,EAAE,iBAAiB,CAAC;IACxB,qFAAqF;IACrF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,+BAoCA;AAED,OAAO,EAAE,cAAc,EAAE,CAAC"}
@@ -10,7 +10,7 @@ import { cn } from "../lib/utils.js";
10
10
  * @family Status
11
11
  * @when The first cell of every collection row: the mark, the name, and one muted line saying what the record is or where it comes from, so the row reads without a heading.
12
12
  * @instead An Agent in a gallery is `AgentListRow`; a model is `ModelIdentity`. A descriptive item with prose and actions is `Item`.
13
- * @behaviour The mark stands 16 px before the name. The name is semibold (600) ink and the detail a muted 14 px line, two lines with no gap between them, so the row is an entity row; `mark` is required; a record with nothing to say under its name leaves `detail` out and is the name alone, never a dash, which is a table cell's missing value and not a record's. The name holds one line and ends in an ellipsis, whole in its title, with a `hint` such as an ID read after it. The detail is one line too and ends in an ellipsis, whole in its title when it is text; in a row on a card narrower than its columns, the facts that leave the row join it after a middle dot, so the record stays the name over one line, and a status that leaves the row stands beside the name, which ends in an ellipsis before it. `wraps` only when the name is the whole record, such as a memory, and then the detail wraps too. The table owns the link.
13
+ * @behaviour The mark stands 16 px before the name. The name is semibold (600) ink and the detail a muted 14 px line, two lines with no gap between them, so the row is an entity row; `mark` is required; a record with nothing to say under its name leaves `detail` out and is the name alone, never a dash, which is a table cell's missing value and not a record's. The name holds one line and ends in an ellipsis, whole in its title, with a `hint` such as an ID read after it. The detail is one line too and ends in an ellipsis, whole in its title when it is text; in a row on a card narrower than its columns, the facts that leave the row join it after a middle dot and wrap so every value stays readable, and a status that leaves the row stands beside the name, which ends in an ellipsis before it. `wraps` only when the name is the whole record, such as a memory, and then the detail wraps too. The table owns the link.
14
14
  * @words “Lead the left column with a scannable identifier: an `Avatar` or icon, a Title Case label, then sentence-case secondary metadata (`Member since Mar 14, 2026`).” (https://vercel.com/geist/entity)
15
15
  * Ours: the name as the record calls itself, and the detail in sentence case: kind, then relation, three to six words, "Company skill · every Agent has it".
16
16
  * @access The name is the row link's text; the mark is decorative; the title carries the full name.
@@ -21,9 +21,7 @@ function RecordIdentity({ name, mark, hint, detail, wraps = false, }) {
21
21
  return (_jsxs("span", { className: "flex min-w-0 items-center gap-4", "data-slot": "record-identity", children: [_jsx(IdentityMark, { ...mark, size: 32 }), _jsxs("span", { className: "grid min-w-0 in-[table]:w-0 in-[table]:flex-1", children: [_jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { className: cn("min-w-0 font-semibold text-foreground", wraps ? "break-words whitespace-pre-wrap" : "truncate"), title: wraps ? undefined : title, children: name }), _jsx(TableCompactNameFacts, {})] }), _jsxs("span", { className: cn(
22
22
  // The line is the containing block of the facts' screen-reader labels, so what it cuts
23
23
  // stays cut instead of widening the table.
24
- "relative text-sm text-muted-foreground", wraps
25
- ? "break-words whitespace-normal"
26
- : "truncate has-[[data-wraps]]:break-words has-[[data-wraps]]:whitespace-normal"), "data-slot": "record-identity-detail",
24
+ "relative text-sm text-muted-foreground", wraps ? "break-words whitespace-normal" : "truncate"), "data-slot": "record-identity-detail",
27
25
  // oxlint-disable-next-line anti-slop/no-runtime-typeof -- detail is a ReactNode; only a string can be a title attribute.
28
26
  title: !wraps && typeof detail === "string" ? detail : undefined, children: [detail, _jsx(TableCompactFacts, {})] })] })] }));
29
27
  }
@@ -189,7 +189,7 @@ export type RecordListWithout<TRecord extends RowData, Key extends PropertyKey>
189
189
  * @instead “For tabular data with sortable columns and shared row shape, use `Table` instead.” (https://vercel.com/geist/entity)
190
190
  * “For a static key/value metadata block on a detail page, use `Description`.” (https://vercel.com/geist/entity)
191
191
  * Ours: Geist's `Table` is a `DataTable`: this list has no visible headings and at most three facts, so a fourth fact is a table, and anything more than a few facts lives on the record's page. A screen gives `RecordList` the records and what each one says, never its own toolbar, table, rows or menus, and nothing else draws a record row. Its `Description` is `ObjectFacts`. Events in time are an `ActivityFeed`. Reusable things a person adds from a catalogue are a `LibraryShelf`. A descriptive row with prose is an `Item`.
192
- * @behaviour One bordered card, a 1 px rule between records and none after the last. A row is 72 px, with 16 px above and below, and its columns stand left to right at the widths the list declares, so a person reads every list the same way: the `select` box (40 px, only when the list is picked from, an optional column), the identity (the rest: the one column without a size), the facts (each its `size`), the status (`status.size`), the time (`time.size`, tabular), the `checklist` ring (52 px, only when the record has steps to be ready) and the commands (`commandsSize`; left out, 64 px for the three dots alone). A `checklist` puts the record's `ProgressChecklist` ring just before its commands, where Vercel ends a project's row with its Production Checklist; the ring opens its own drawer, never the row. Every row has its 32 px mark, its name (600) and, under it, one muted 14 px line; `name` and the mark are required; a record with nothing to say under its name leaves the line out and is the name alone, never a dash. Give every column a `size` in pixels; the name takes the rest. With `layout="line"`, as on Vercel's Projects page, the facts and the status share one muted line, joined by a middle dot, in a column as wide as the name's, and the row stays one line (mark, name over its line, that summary, the time, the ring, the three dots) on a card from 448 px, and on a narrower card the summary and the time move under the name as one muted line joined by a middle dot, in the same row, their labels read by assistive technology only. Rows of `columns` follow the same rule once the card is narrower than their columns: the facts and the time leave the row for that one line, ending in an ellipsis, never a labelled list, and the status stands beside the name, which ends in an ellipsis before it, so the exception is never the part a phone's line cuts. `facts` takes at most three. A menu of one command that is not destructive, with no row command, is that command's button, except in a `line` row, which keeps it in the three dots as Vercel's project row does; without a `commandsSize` the column is as wide as the widest such button, so its label is never cut. Status is only for the exception, empty for the usual record, and its column stands only while a record in the list shows one, so the name and its line keep the room; the commands column likewise stands only while a record has a command, a menu or a message, or the rows open, and it widens for a failed action's next steps only while a row shows one. Never the three dots before the button, a decorative icon for the record's mark, a status on every row, or a destructive button on every row: a destructive command stays in the three dots. A `line` row's summary joins only the facts the record has, so it never ends in a dot. A failed or refused action of the record shows as its catalogue message inside its own row, never a notice under the row: it replaces the detail line, with its next steps beside its commands, or under the message on a card too narrow for the row's columns. With `open` the whole row is one target and takes the `accent` hover, and a row that only opens ends in a muted chevron; a row that opens nothing never reacts to hover. With `editor` the record's row opens its form under its line. `empty` is required: `null` is the choice to draw nothing for a list with no records, and any other value is the state it draws. A note about the whole list is `footer.note`: one muted line, an optional *Learn more* after it at the left and one secondary action at the right, in a strip of its own under the count and the paging, as Vercel ends its deployments list ("Deployment retention is enabled. Learn more", *View recently deleted*). Colour is neutral, the name in ink and every other cell in the body grey, except the status badge and a failed action's message.
192
+ * @behaviour One bordered card, a 1 px rule between records and none after the last. A row is 72 px, with 16 px above and below, and its columns stand left to right at the widths the list declares, so a person reads every list the same way: the `select` box (40 px, only when the list is picked from, an optional column), the identity (the rest: the one column without a size), the facts (each its `size`), the status (`status.size`), the time (`time.size`, tabular), the `checklist` ring (52 px, only when the record has steps to be ready) and the commands (`commandsSize`; left out, 64 px for the three dots alone). A `checklist` puts the record's `ProgressChecklist` ring just before its commands, where Vercel ends a project's row with its Production Checklist; the ring opens its own drawer, never the row. Every row has its 32 px mark, its name (600) and, under it, one muted 14 px line; `name` and the mark are required; a record with nothing to say under its name leaves the line out and is the name alone, never a dash. Give every column a `size` in pixels; the name takes the rest. With `layout="line"`, as on Vercel's Projects page, the facts and the status share one muted line, joined by a middle dot, in a column as wide as the name's, and the row stays one line (mark, name over its line, that summary, the time, the ring, the three dots) on a card from 448 px, and on a narrower card the summary and the time move under the name as muted text joined by a middle dot that wraps, in the same row, their labels read by assistive technology only. Rows of `columns` follow the same rule once the card is narrower than their columns: the facts and the time leave the row for that muted text, wrapping so every value stays readable, never a labelled list, and the status stands beside the name, which ends in an ellipsis before it, so the exception is never the part a phone's line cuts. `facts` takes at most three. A menu of one command that is not destructive, with no row command, is that command's button, except in a `line` row, which keeps it in the three dots as Vercel's project row does; without a `commandsSize` the column is as wide as the widest such button, so its label is never cut. Status is only for the exception, empty for the usual record, and its column stands only while a record in the list shows one, so the name and its line keep the room; the commands column likewise stands only while a record has a command, a menu or a message, or the rows open, and it widens for a failed action's next steps only while a row shows one. Never the three dots before the button, a decorative icon for the record's mark, a status on every row, or a destructive button on every row: a destructive command stays in the three dots. A `line` row's summary joins only the facts the record has, so it never ends in a dot. A failed or refused action of the record shows as its catalogue message inside its own row, never a notice under the row: it replaces the detail line, with its next steps beside its commands, or under the message on a card too narrow for the row's columns. With `open` the whole row is one target and takes the `accent` hover, and a row that only opens ends in a muted chevron; a row that opens nothing never reacts to hover. With `editor` the record's row opens its form under its line. `empty` is required: `null` is the choice to draw nothing for a list with no records, and any other value is the state it draws. A note about the whole list is `footer.note`: one muted line, an optional *Learn more* after it at the left and one secondary action at the right, in a strip of its own under the count and the paging, as Vercel ends its deployments list ("Deployment retention is enabled. Learn more", *View recently deleted*). Colour is neutral, the name in ink and every other cell in the body grey, except the status badge and a failed action's message.
193
193
  * “Pick the variant by what the user needs: no-results for a filtered list that returned zero rows, blank slate or informational for a resource the user hasn’t created, cleared for completed work, permission for role or tier denials, error for a failed load.” (https://vercel.com/geist/empty-state)
194
194
  * Ours: `failed`, `noMatch` and `empty` are those variants, drawn inside the card.
195
195
  * “The right column holds at most one or two controls. If the row needs more, move secondary actions into a `Dots Menu`.” (https://vercel.com/geist/entity)
@@ -250,7 +250,7 @@ function RecordRows(props) {
250
250
  * @instead “For tabular data with sortable columns and shared row shape, use `Table` instead.” (https://vercel.com/geist/entity)
251
251
  * “For a static key/value metadata block on a detail page, use `Description`.” (https://vercel.com/geist/entity)
252
252
  * Ours: Geist's `Table` is a `DataTable`: this list has no visible headings and at most three facts, so a fourth fact is a table, and anything more than a few facts lives on the record's page. A screen gives `RecordList` the records and what each one says, never its own toolbar, table, rows or menus, and nothing else draws a record row. Its `Description` is `ObjectFacts`. Events in time are an `ActivityFeed`. Reusable things a person adds from a catalogue are a `LibraryShelf`. A descriptive row with prose is an `Item`.
253
- * @behaviour One bordered card, a 1 px rule between records and none after the last. A row is 72 px, with 16 px above and below, and its columns stand left to right at the widths the list declares, so a person reads every list the same way: the `select` box (40 px, only when the list is picked from, an optional column), the identity (the rest: the one column without a size), the facts (each its `size`), the status (`status.size`), the time (`time.size`, tabular), the `checklist` ring (52 px, only when the record has steps to be ready) and the commands (`commandsSize`; left out, 64 px for the three dots alone). A `checklist` puts the record's `ProgressChecklist` ring just before its commands, where Vercel ends a project's row with its Production Checklist; the ring opens its own drawer, never the row. Every row has its 32 px mark, its name (600) and, under it, one muted 14 px line; `name` and the mark are required; a record with nothing to say under its name leaves the line out and is the name alone, never a dash. Give every column a `size` in pixels; the name takes the rest. With `layout="line"`, as on Vercel's Projects page, the facts and the status share one muted line, joined by a middle dot, in a column as wide as the name's, and the row stays one line (mark, name over its line, that summary, the time, the ring, the three dots) on a card from 448 px, and on a narrower card the summary and the time move under the name as one muted line joined by a middle dot, in the same row, their labels read by assistive technology only. Rows of `columns` follow the same rule once the card is narrower than their columns: the facts and the time leave the row for that one line, ending in an ellipsis, never a labelled list, and the status stands beside the name, which ends in an ellipsis before it, so the exception is never the part a phone's line cuts. `facts` takes at most three. A menu of one command that is not destructive, with no row command, is that command's button, except in a `line` row, which keeps it in the three dots as Vercel's project row does; without a `commandsSize` the column is as wide as the widest such button, so its label is never cut. Status is only for the exception, empty for the usual record, and its column stands only while a record in the list shows one, so the name and its line keep the room; the commands column likewise stands only while a record has a command, a menu or a message, or the rows open, and it widens for a failed action's next steps only while a row shows one. Never the three dots before the button, a decorative icon for the record's mark, a status on every row, or a destructive button on every row: a destructive command stays in the three dots. A `line` row's summary joins only the facts the record has, so it never ends in a dot. A failed or refused action of the record shows as its catalogue message inside its own row, never a notice under the row: it replaces the detail line, with its next steps beside its commands, or under the message on a card too narrow for the row's columns. With `open` the whole row is one target and takes the `accent` hover, and a row that only opens ends in a muted chevron; a row that opens nothing never reacts to hover. With `editor` the record's row opens its form under its line. `empty` is required: `null` is the choice to draw nothing for a list with no records, and any other value is the state it draws. A note about the whole list is `footer.note`: one muted line, an optional *Learn more* after it at the left and one secondary action at the right, in a strip of its own under the count and the paging, as Vercel ends its deployments list ("Deployment retention is enabled. Learn more", *View recently deleted*). Colour is neutral, the name in ink and every other cell in the body grey, except the status badge and a failed action's message.
253
+ * @behaviour One bordered card, a 1 px rule between records and none after the last. A row is 72 px, with 16 px above and below, and its columns stand left to right at the widths the list declares, so a person reads every list the same way: the `select` box (40 px, only when the list is picked from, an optional column), the identity (the rest: the one column without a size), the facts (each its `size`), the status (`status.size`), the time (`time.size`, tabular), the `checklist` ring (52 px, only when the record has steps to be ready) and the commands (`commandsSize`; left out, 64 px for the three dots alone). A `checklist` puts the record's `ProgressChecklist` ring just before its commands, where Vercel ends a project's row with its Production Checklist; the ring opens its own drawer, never the row. Every row has its 32 px mark, its name (600) and, under it, one muted 14 px line; `name` and the mark are required; a record with nothing to say under its name leaves the line out and is the name alone, never a dash. Give every column a `size` in pixels; the name takes the rest. With `layout="line"`, as on Vercel's Projects page, the facts and the status share one muted line, joined by a middle dot, in a column as wide as the name's, and the row stays one line (mark, name over its line, that summary, the time, the ring, the three dots) on a card from 448 px, and on a narrower card the summary and the time move under the name as muted text joined by a middle dot that wraps, in the same row, their labels read by assistive technology only. Rows of `columns` follow the same rule once the card is narrower than their columns: the facts and the time leave the row for that muted text, wrapping so every value stays readable, never a labelled list, and the status stands beside the name, which ends in an ellipsis before it, so the exception is never the part a phone's line cuts. `facts` takes at most three. A menu of one command that is not destructive, with no row command, is that command's button, except in a `line` row, which keeps it in the three dots as Vercel's project row does; without a `commandsSize` the column is as wide as the widest such button, so its label is never cut. Status is only for the exception, empty for the usual record, and its column stands only while a record in the list shows one, so the name and its line keep the room; the commands column likewise stands only while a record has a command, a menu or a message, or the rows open, and it widens for a failed action's next steps only while a row shows one. Never the three dots before the button, a decorative icon for the record's mark, a status on every row, or a destructive button on every row: a destructive command stays in the three dots. A `line` row's summary joins only the facts the record has, so it never ends in a dot. A failed or refused action of the record shows as its catalogue message inside its own row, never a notice under the row: it replaces the detail line, with its next steps beside its commands, or under the message on a card too narrow for the row's columns. With `open` the whole row is one target and takes the `accent` hover, and a row that only opens ends in a muted chevron; a row that opens nothing never reacts to hover. With `editor` the record's row opens its form under its line. `empty` is required: `null` is the choice to draw nothing for a list with no records, and any other value is the state it draws. A note about the whole list is `footer.note`: one muted line, an optional *Learn more* after it at the left and one secondary action at the right, in a strip of its own under the count and the paging, as Vercel ends its deployments list ("Deployment retention is enabled. Learn more", *View recently deleted*). Colour is neutral, the name in ink and every other cell in the body grey, except the status badge and a failed action's message.
254
254
  * “Pick the variant by what the user needs: no-results for a filtered list that returned zero rows, blank slate or informational for a resource the user hasn’t created, cleared for completed work, permission for role or tier denials, error for a failed load.” (https://vercel.com/geist/empty-state)
255
255
  * Ours: `failed`, `noMatch` and `empty` are those variants, drawn inside the card.
256
256
  * “The right column holds at most one or two controls. If the row needs more, move secondary actions into a `Dots Menu`.” (https://vercel.com/geist/entity)
@@ -31,7 +31,7 @@ export type ScopeSwitcherProps = {
31
31
  * @family Navigation
32
32
  * @when Changing which part of the account a page reads, such as Analytics' scope, or switching between objects from the shell's top bar with `variant="navigation"`.
33
33
  * @instead A viewer with one scope and nothing to create reads its name with no control, which this draws by itself. Finding anything is `CommandDialog`; narrowing rows is `CollectionFilters`.
34
- * @behaviour A ghost trigger, at most 16rem, opens a 288 px popover with focus in its search. Navigation omits the visible prefix, carries each option's identity mark and opens a 384 px popover constrained to the viewport. Type to filter, Enter chooses and closes; choosing the current one changes nothing. The caller writes the choice to the URL in `onValueChange`, so sibling pages keep it. An optional create command sits last.
34
+ * @behaviour A ghost trigger, at most 16rem, opens a 288 px popover with focus in its search. Navigation omits the visible prefix, wraps the selected name on phones instead of cutting it, carries each option's identity mark and opens a 384 px popover constrained to the viewport. Type to filter, Enter chooses and closes; choosing the current one changes nothing. The caller writes the choice to the URL in `onValueChange`, so sibling pages keep it. An optional create command sits last.
35
35
  * @words `copy.label` names what is chosen ("Scope") beside the current scope's name. Search and empty lines name the thing: "Find a scope", "No scope matches".
36
36
  * @access The trigger reads "Scope: All customers"; the popover has a hidden title; options are a listbox with the current one checked.
37
37
  */
@@ -1 +1 @@
1
- {"version":3,"file":"scope-switcher.d.ts","sourceRoot":"","sources":["../../src/components/scope-switcher.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAgB/B,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,+FAA+F;IAC/F,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IAChC,8DAA8D;IAC9D,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,IAAI,EAAE,iBAAiB,CAAC;IACxB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;IACjD,OAAO,CAAC,EAAE,OAAO,GAAG,YAAY,CAAC;CAClC,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,OAAO,EACP,aAAa,EACb,IAAI,EACJ,MAAM,EACN,OAAiB,GAClB,EAAE,kBAAkB,qBA0GpB"}
1
+ {"version":3,"file":"scope-switcher.d.ts","sourceRoot":"","sources":["../../src/components/scope-switcher.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAgB/B,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,+FAA+F;IAC/F,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IAChC,8DAA8D;IAC9D,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,IAAI,EAAE,iBAAiB,CAAC;IACxB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;IACjD,OAAO,CAAC,EAAE,OAAO,GAAG,YAAY,CAAC;CAClC,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,OAAO,EACP,aAAa,EACb,IAAI,EACJ,MAAM,EACN,OAAiB,GAClB,EAAE,kBAAkB,qBAsHpB"}
@@ -12,7 +12,7 @@ import { cn } from "../lib/utils.js";
12
12
  * @family Navigation
13
13
  * @when Changing which part of the account a page reads, such as Analytics' scope, or switching between objects from the shell's top bar with `variant="navigation"`.
14
14
  * @instead A viewer with one scope and nothing to create reads its name with no control, which this draws by itself. Finding anything is `CommandDialog`; narrowing rows is `CollectionFilters`.
15
- * @behaviour A ghost trigger, at most 16rem, opens a 288 px popover with focus in its search. Navigation omits the visible prefix, carries each option's identity mark and opens a 384 px popover constrained to the viewport. Type to filter, Enter chooses and closes; choosing the current one changes nothing. The caller writes the choice to the URL in `onValueChange`, so sibling pages keep it. An optional create command sits last.
15
+ * @behaviour A ghost trigger, at most 16rem, opens a 288 px popover with focus in its search. Navigation omits the visible prefix, wraps the selected name on phones instead of cutting it, carries each option's identity mark and opens a 384 px popover constrained to the viewport. Type to filter, Enter chooses and closes; choosing the current one changes nothing. The caller writes the choice to the URL in `onValueChange`, so sibling pages keep it. An optional create command sits last.
16
16
  * @words `copy.label` names what is chosen ("Scope") beside the current scope's name. Search and empty lines name the thing: "Find a scope", "No scope matches".
17
17
  * @access The trigger reads "Scope: All customers"; the popover has a hidden title; options are a listbox with the current one checked.
18
18
  */
@@ -36,7 +36,10 @@ export function ScopeSwitcher({ value, options, onValueChange, copy, create, var
36
36
  onValueChange(next);
37
37
  };
38
38
  const switchable = options.length > 1 || create !== undefined;
39
- return (_jsxs("span", { "data-slot": "scope-switcher", className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { "aria-hidden": switchable ? "true" : undefined, className: cn("text-xs font-medium text-muted-foreground", variant === "navigation" && "sr-only"), children: copy.label }), switchable ? (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsxs(PopoverTrigger, { render: _jsx(Button, { variant: "ghost", className: "min-w-0 max-w-64 shrink", prefix: current?.mark ? _jsx("span", { "aria-hidden": "true", children: current.mark }) : undefined, suffix: _jsx(ChevronsUpDown, { "aria-hidden": "true" }) }), children: [_jsxs("span", { className: "sr-only", children: [copy.label, ":"] }), " ", _jsx("span", { className: "truncate", children: currentLabel })] }), _jsxs(PopoverContent, { align: "start", className: cn("w-72 max-w-[calc(100vw-2rem)] p-0", variant === "navigation" && "w-96"), initialFocus: search, children: [_jsx(PopoverTitle, { className: "sr-only", children: copy.label }), _jsxs(Command, { label: copy.search, resultCount: copy.results, children: [_jsx(CommandInput, { ref: search, "aria-label": copy.search, placeholder: copy.search }), _jsxs(CommandList, { label: copy.label, children: [_jsx(CommandEmpty, { children: copy.empty }), groups.map(([group, items]) => (_jsx(CommandGroup, { heading: group, children: items.map((option) => (_jsxs(CommandItem, { value: option.value, keywords: [
39
+ return (_jsxs("span", { "data-slot": "scope-switcher", className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { "aria-hidden": switchable ? "true" : undefined, className: cn("text-xs font-medium text-muted-foreground", variant === "navigation" && "sr-only"), children: copy.label }), switchable ? (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsxs(PopoverTrigger, { render: _jsx(Button, { variant: "ghost", className: cn("min-w-0 max-w-64 shrink", variant === "navigation" &&
40
+ "h-auto max-w-full py-1 md:h-control-height md:max-w-64 md:py-0"), prefix: current?.mark ? _jsx("span", { "aria-hidden": "true", children: current.mark }) : undefined, suffix: _jsx(ChevronsUpDown, { "aria-hidden": "true" }) }), children: [_jsxs("span", { className: "sr-only", children: [copy.label, ":"] }), " ", _jsx("span", { className: cn(variant === "navigation"
41
+ ? "whitespace-normal break-words text-left md:truncate"
42
+ : "truncate"), children: currentLabel })] }), _jsxs(PopoverContent, { align: "start", className: cn("w-72 max-w-[calc(100vw-2rem)] p-0", variant === "navigation" && "w-96"), initialFocus: search, children: [_jsx(PopoverTitle, { className: "sr-only", children: copy.label }), _jsxs(Command, { label: copy.search, resultCount: copy.results, children: [_jsx(CommandInput, { ref: search, "aria-label": copy.search, placeholder: copy.search }), _jsxs(CommandList, { label: copy.label, children: [_jsx(CommandEmpty, { children: copy.empty }), groups.map(([group, items]) => (_jsx(CommandGroup, { heading: group, children: items.map((option) => (_jsxs(CommandItem, { value: option.value, keywords: [
40
43
  option.label,
41
44
  ...(option.group ? [option.group] : []),
42
45
  ...(option.keywords ?? []),
@@ -6,7 +6,7 @@ export declare const tableCompactLayouts: {
6
6
  readonly track: "hidden @md/table:table-column";
7
7
  readonly row: "@max-md/table:[&>td]:border-b-0 @max-md/table:[&>td]:after:hidden @max-md/table:h-auto! @max-md/table:[&>td]:pb-1!";
8
8
  readonly details: "@md/table:hidden";
9
- readonly table: "@max-md/table:table-auto @max-md/table:[&_td]:whitespace-normal @max-md/table:[&_th:not(:first-child):not([data-identity])]:w-px @max-md/table:[&_tr:not([data-slot=table-detail-row])>td:not(:first-child)]:pl-0 @max-md/table:[&_tr:not([data-slot=table-detail-row])>td:not(:first-child):not(:last-child)]:pr-0";
9
+ readonly table: "@max-md/table:table-auto @max-md/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-md/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-md/table:[&_td]:whitespace-normal @max-md/table:[&_th:not(:first-child):not([data-identity])]:w-px @max-md/table:[&_tr:not([data-slot=table-detail-row])>td:not(:first-child)]:pl-0 @max-md/table:[&_tr:not([data-slot=table-detail-row])>td:not(:first-child):not(:last-child)]:pr-0";
10
10
  };
11
11
  readonly tight: {
12
12
  readonly width: 576;
@@ -14,7 +14,7 @@ export declare const tableCompactLayouts: {
14
14
  readonly track: "hidden @xl/table:table-column";
15
15
  readonly row: "@max-xl/table:[&>td]:border-b-0 @max-xl/table:[&>td]:after:hidden @max-xl/table:h-auto! @max-xl/table:[&>td]:pb-1!";
16
16
  readonly details: "@xl/table:hidden";
17
- readonly table: "@max-xl/table:table-auto @max-xl/table:[&_td]:whitespace-normal @max-xl/table:[&_th:not(:first-child):not([data-identity])]:w-px";
17
+ readonly table: "@max-xl/table:table-auto @max-xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-xl/table:[&_td]:whitespace-normal @max-xl/table:[&_th:not(:first-child):not([data-identity])]:w-px";
18
18
  };
19
19
  readonly standard: {
20
20
  readonly width: 768;
@@ -22,7 +22,7 @@ export declare const tableCompactLayouts: {
22
22
  readonly track: "hidden @3xl/table:table-column";
23
23
  readonly row: "@max-3xl/table:[&>td]:border-b-0 @max-3xl/table:[&>td]:after:hidden @max-3xl/table:h-auto! @max-3xl/table:[&>td]:pb-1!";
24
24
  readonly details: "@3xl/table:hidden";
25
- readonly table: "@max-3xl/table:table-auto @max-3xl/table:[&_td]:whitespace-normal @max-3xl/table:[&_th:not(:first-child):not([data-identity])]:w-px";
25
+ readonly table: "@max-3xl/table:table-auto @max-3xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-3xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-3xl/table:[&_td]:whitespace-normal @max-3xl/table:[&_th:not(:first-child):not([data-identity])]:w-px";
26
26
  };
27
27
  readonly wide: {
28
28
  readonly width: 1024;
@@ -30,7 +30,7 @@ export declare const tableCompactLayouts: {
30
30
  readonly track: "hidden @5xl/table:table-column";
31
31
  readonly row: "@max-5xl/table:[&>td]:border-b-0 @max-5xl/table:[&>td]:after:hidden @max-5xl/table:h-auto! @max-5xl/table:[&>td]:pb-1!";
32
32
  readonly details: "@5xl/table:hidden";
33
- readonly table: "@max-5xl/table:table-auto @max-5xl/table:[&_td]:whitespace-normal @max-5xl/table:[&_th:not(:first-child):not([data-identity])]:w-px";
33
+ readonly table: "@max-5xl/table:table-auto @max-5xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-5xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-5xl/table:[&_td]:whitespace-normal @max-5xl/table:[&_th:not(:first-child):not([data-identity])]:w-px";
34
34
  };
35
35
  readonly roomy: {
36
36
  readonly width: 1088;
@@ -38,7 +38,7 @@ export declare const tableCompactLayouts: {
38
38
  readonly track: "hidden @min-[68rem]/table:table-column";
39
39
  readonly row: "@max-[68rem]/table:[&>td]:border-b-0 @max-[68rem]/table:[&>td]:after:hidden @max-[68rem]/table:h-auto! @max-[68rem]/table:[&>td]:pb-1!";
40
40
  readonly details: "@min-[68rem]/table:hidden";
41
- readonly table: "@max-[68rem]/table:table-auto @max-[68rem]/table:[&_td]:whitespace-normal @max-[68rem]/table:[&_th:not(:first-child):not([data-identity])]:w-px";
41
+ readonly table: "@max-[68rem]/table:table-auto @max-[68rem]/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-[68rem]/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-[68rem]/table:[&_td]:whitespace-normal @max-[68rem]/table:[&_th:not(:first-child):not([data-identity])]:w-px";
42
42
  };
43
43
  readonly spacious: {
44
44
  readonly width: 1152;
@@ -46,14 +46,14 @@ export declare const tableCompactLayouts: {
46
46
  readonly track: "hidden @6xl/table:table-column";
47
47
  readonly row: "@max-6xl/table:[&>td]:border-b-0 @max-6xl/table:[&>td]:after:hidden @max-6xl/table:h-auto! @max-6xl/table:[&>td]:pb-1!";
48
48
  readonly details: "@6xl/table:hidden";
49
- readonly table: "@max-6xl/table:table-auto @max-6xl/table:[&_td]:whitespace-normal @max-6xl/table:[&_th:not(:first-child):not([data-identity])]:w-px";
49
+ readonly table: "@max-6xl/table:table-auto @max-6xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-6xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-6xl/table:[&_td]:whitespace-normal @max-6xl/table:[&_th:not(:first-child):not([data-identity])]:w-px";
50
50
  };
51
51
  };
52
52
  export type TableCompactLayout = keyof typeof tableCompactLayouts;
53
53
  /**
54
54
  * The compact layout for columns of these sizes: the narrowest card width at which every sized
55
55
  * column, and a floor for each column without a size, still fit. A card narrower than that
56
- * leaves its secondary columns out of the row: each joins the one muted line under the name, or is
56
+ * leaves its secondary columns out of the row: each joins the muted text under the name, or is
57
57
  * dropped.
58
58
  */
59
59
  export declare function compactLayoutFor(sizes: readonly (number | undefined)[]): TableCompactLayout;
@@ -6,7 +6,7 @@ export const tableCompactLayouts = {
6
6
  track: "hidden @md/table:table-column",
7
7
  row: "@max-md/table:[&>td]:border-b-0 @max-md/table:[&>td]:after:hidden @max-md/table:h-auto! @max-md/table:[&>td]:pb-1!",
8
8
  details: "@md/table:hidden",
9
- table: "@max-md/table:table-auto @max-md/table:[&_td]:whitespace-normal @max-md/table:[&_th:not(:first-child):not([data-identity])]:w-px @max-md/table:[&_tr:not([data-slot=table-detail-row])>td:not(:first-child)]:pl-0 @max-md/table:[&_tr:not([data-slot=table-detail-row])>td:not(:first-child):not(:last-child)]:pr-0",
9
+ table: "@max-md/table:table-auto @max-md/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-md/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-md/table:[&_td]:whitespace-normal @max-md/table:[&_th:not(:first-child):not([data-identity])]:w-px @max-md/table:[&_tr:not([data-slot=table-detail-row])>td:not(:first-child)]:pl-0 @max-md/table:[&_tr:not([data-slot=table-detail-row])>td:not(:first-child):not(:last-child)]:pr-0",
10
10
  },
11
11
  tight: {
12
12
  width: 576,
@@ -14,7 +14,7 @@ export const tableCompactLayouts = {
14
14
  track: "hidden @xl/table:table-column",
15
15
  row: "@max-xl/table:[&>td]:border-b-0 @max-xl/table:[&>td]:after:hidden @max-xl/table:h-auto! @max-xl/table:[&>td]:pb-1!",
16
16
  details: "@xl/table:hidden",
17
- table: "@max-xl/table:table-auto @max-xl/table:[&_td]:whitespace-normal @max-xl/table:[&_th:not(:first-child):not([data-identity])]:w-px",
17
+ table: "@max-xl/table:table-auto @max-xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-xl/table:[&_td]:whitespace-normal @max-xl/table:[&_th:not(:first-child):not([data-identity])]:w-px",
18
18
  },
19
19
  standard: {
20
20
  width: 768,
@@ -22,7 +22,7 @@ export const tableCompactLayouts = {
22
22
  track: "hidden @3xl/table:table-column",
23
23
  row: "@max-3xl/table:[&>td]:border-b-0 @max-3xl/table:[&>td]:after:hidden @max-3xl/table:h-auto! @max-3xl/table:[&>td]:pb-1!",
24
24
  details: "@3xl/table:hidden",
25
- table: "@max-3xl/table:table-auto @max-3xl/table:[&_td]:whitespace-normal @max-3xl/table:[&_th:not(:first-child):not([data-identity])]:w-px",
25
+ table: "@max-3xl/table:table-auto @max-3xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-3xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-3xl/table:[&_td]:whitespace-normal @max-3xl/table:[&_th:not(:first-child):not([data-identity])]:w-px",
26
26
  },
27
27
  wide: {
28
28
  width: 1024,
@@ -30,7 +30,7 @@ export const tableCompactLayouts = {
30
30
  track: "hidden @5xl/table:table-column",
31
31
  row: "@max-5xl/table:[&>td]:border-b-0 @max-5xl/table:[&>td]:after:hidden @max-5xl/table:h-auto! @max-5xl/table:[&>td]:pb-1!",
32
32
  details: "@5xl/table:hidden",
33
- table: "@max-5xl/table:table-auto @max-5xl/table:[&_td]:whitespace-normal @max-5xl/table:[&_th:not(:first-child):not([data-identity])]:w-px",
33
+ table: "@max-5xl/table:table-auto @max-5xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-5xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-5xl/table:[&_td]:whitespace-normal @max-5xl/table:[&_th:not(:first-child):not([data-identity])]:w-px",
34
34
  },
35
35
  roomy: {
36
36
  width: 1088,
@@ -38,7 +38,7 @@ export const tableCompactLayouts = {
38
38
  track: "hidden @min-[68rem]/table:table-column",
39
39
  row: "@max-[68rem]/table:[&>td]:border-b-0 @max-[68rem]/table:[&>td]:after:hidden @max-[68rem]/table:h-auto! @max-[68rem]/table:[&>td]:pb-1!",
40
40
  details: "@min-[68rem]/table:hidden",
41
- table: "@max-[68rem]/table:table-auto @max-[68rem]/table:[&_td]:whitespace-normal @max-[68rem]/table:[&_th:not(:first-child):not([data-identity])]:w-px",
41
+ table: "@max-[68rem]/table:table-auto @max-[68rem]/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-[68rem]/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-[68rem]/table:[&_td]:whitespace-normal @max-[68rem]/table:[&_th:not(:first-child):not([data-identity])]:w-px",
42
42
  },
43
43
  spacious: {
44
44
  width: 1152,
@@ -46,7 +46,7 @@ export const tableCompactLayouts = {
46
46
  track: "hidden @6xl/table:table-column",
47
47
  row: "@max-6xl/table:[&>td]:border-b-0 @max-6xl/table:[&>td]:after:hidden @max-6xl/table:h-auto! @max-6xl/table:[&>td]:pb-1!",
48
48
  details: "@6xl/table:hidden",
49
- table: "@max-6xl/table:table-auto @max-6xl/table:[&_td]:whitespace-normal @max-6xl/table:[&_th:not(:first-child):not([data-identity])]:w-px",
49
+ table: "@max-6xl/table:table-auto @max-6xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-6xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-6xl/table:[&_td]:whitespace-normal @max-6xl/table:[&_th:not(:first-child):not([data-identity])]:w-px",
50
50
  },
51
51
  };
52
52
  /** The least a column without a size needs, in pixels: below it the card lays out compact. */
@@ -61,7 +61,7 @@ const NARROWEST_FIRST = [
61
61
  /**
62
62
  * The compact layout for columns of these sizes: the narrowest card width at which every sized
63
63
  * column, and a floor for each column without a size, still fit. A card narrower than that
64
- * leaves its secondary columns out of the row: each joins the one muted line under the name, or is
64
+ * leaves its secondary columns out of the row: each joins the muted text under the name, or is
65
65
  * dropped.
66
66
  */
67
67
  export function compactLayoutFor(sizes) {
@@ -39,8 +39,8 @@ type TableName = AccessibleName | {
39
39
  * it, one muted line joined by a middle dot that ends in an ellipsis, their labels read by assistive
40
40
  * technology only, as Vercel's lists read on a phone; a `Tag` there reads as its plain text, and a
41
41
  * fact that `wraps` lets the line wrap whole instead of ending in an ellipsis. An entity row
42
- * (`density="comfortable"`) keeps its record on one 72 px row instead: its facts join the line
43
- * under the name that `RecordIdentity` draws (`TableCompactLine`), so the row is the name over one line. Colour is neutral throughout: headings and cells share
42
+ * (`density="comfortable"`) keeps its facts in the identity cell instead: they join the text
43
+ * under the name that `RecordIdentity` draws (`TableCompactLine`), and wrap so every value stays readable. Colour is neutral throughout: headings and cells share
44
44
  * the body grey and a heading differs by weight, the footer is ink, and the hue of a state lives in
45
45
  * the `StatusBadge` of a cell, never in the row.
46
46
  * “Sortable column headers are buttons. The visible label stays Title Case; the sort-direction arrow is decorative and the button announces the next sort state to assistive tech.” (https://vercel.com/geist/table)
@@ -37,8 +37,8 @@ import { titleWhenCut } from "../internal/title-when-cut.js";
37
37
  * it, one muted line joined by a middle dot that ends in an ellipsis, their labels read by assistive
38
38
  * technology only, as Vercel's lists read on a phone; a `Tag` there reads as its plain text, and a
39
39
  * fact that `wraps` lets the line wrap whole instead of ending in an ellipsis. An entity row
40
- * (`density="comfortable"`) keeps its record on one 72 px row instead: its facts join the line
41
- * under the name that `RecordIdentity` draws (`TableCompactLine`), so the row is the name over one line. Colour is neutral throughout: headings and cells share
40
+ * (`density="comfortable"`) keeps its facts in the identity cell instead: they join the text
41
+ * under the name that `RecordIdentity` draws (`TableCompactLine`), and wrap so every value stays readable. Colour is neutral throughout: headings and cells share
42
42
  * the body grey and a heading differs by weight, the footer is ink, and the hue of a state lives in
43
43
  * the `StatusBadge` of a cell, never in the row.
44
44
  * “Sortable column headers are buttons. The visible label stays Title Case; the sort-direction arrow is decorative and the button announces the next sort state to assistive tech.” (https://vercel.com/geist/table)
@@ -17,11 +17,6 @@ type TableCompactLineValue = {
17
17
  layout: TableCompactLayout;
18
18
  facts: readonly TableDetailFact[];
19
19
  };
20
- /**
21
- * The facts an entity row moves onto the line under its name on a card narrower than its
22
- * columns, so the record stays one 72 px row: the name over one muted line. `RecordIdentity`
23
- * draws them after its own line.
24
- */
25
20
  export declare function TableCompactLine({ children, ...value }: TableCompactLineValue & {
26
21
  children: React.ReactNode;
27
22
  }): React.JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"table-compact-line.d.ts","sourceRoot":"","sources":["../../src/internal/table-compact-line.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAG1F,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,4HAA4H;IAC5H,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wGAAwG;IACxG,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,gBAAwB,GACzB,EAAE;IACD,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;IAClC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,uBAQA;AAED,KAAK,qBAAqB,GAAG;IAAE,MAAM,EAAE,kBAAkB,CAAC;IAAC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAA;CAAE,CAAC;AAI/F;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,qBAAqB,GAAG;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,qBAEvD;AAED,4IAA4I;AAC5I,wBAAgB,iBAAiB,6BAiBhC;AAED,2KAA2K;AAC3K,wBAAgB,qBAAqB,6BAkBpC;AAID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,qBAEA;AAED,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD"}
1
+ {"version":3,"file":"table-compact-line.d.ts","sourceRoot":"","sources":["../../src/internal/table-compact-line.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAG1F,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,4HAA4H;IAC5H,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wGAAwG;IACxG,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,gBAAwB,GACzB,EAAE;IACD,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;IAClC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,uBAQA;AAED,KAAK,qBAAqB,GAAG;IAAE,MAAM,EAAE,kBAAkB,CAAC;IAAC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAA;CAAE,CAAC;AAI/F,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,qBAAqB,GAAG;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,qBAEvD;AAED,4IAA4I;AAC5I,wBAAgB,iBAAiB,6BAgBhC;AAED,2KAA2K;AAC3K,wBAAgB,qBAAqB,6BAkBpC;AAID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,qBAEA;AAED,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD"}
@@ -7,11 +7,6 @@ export function DetailFacts({ facts, leadingSeparator = false, }) {
7
7
  return facts.map((fact, index) => (_jsxs("span", { children: [index > 0 || leadingSeparator ? _jsx("span", { "aria-hidden": "true", children: " \u00B7 " }) : null, _jsxs("span", { className: "sr-only", children: [fact.label, ": "] }), fact.value] }, fact.id)));
8
8
  }
9
9
  const TableCompactLineContext = React.createContext(null);
10
- /**
11
- * The facts an entity row moves onto the line under its name on a card narrower than its
12
- * columns, so the record stays one 72 px row: the name over one muted line. `RecordIdentity`
13
- * draws them after its own line.
14
- */
15
10
  export function TableCompactLine({ children, ...value }) {
16
11
  return _jsx(TableCompactLineContext, { value: value, children: children });
17
12
  }
@@ -23,7 +18,7 @@ export function TableCompactFacts() {
23
18
  const facts = line.facts.filter((fact) => !fact.besideName);
24
19
  if (facts.length === 0)
25
20
  return null;
26
- return (_jsx("span", { className: cn("[&_[data-slot=tag]]:contents [&>span>:is(div,p,.block,.grid,.flex)]:inline", tableCompactLayouts[line.layout].details), "data-slot": "table-detail-line", "data-wraps": facts.some((fact) => fact.wraps) || undefined, children: _jsx(DetailFacts, { facts: facts, leadingSeparator: true }) }));
21
+ return (_jsx("span", { className: cn("[&_[data-slot=tag]]:contents [&>span>:is(div,p,.block,.grid,.flex)]:inline", tableCompactLayouts[line.layout].details), "data-slot": "table-detail-line", children: _jsx(DetailFacts, { facts: facts, leadingSeparator: true }) }));
27
22
  }
28
23
  /** The compact facts that stand beside the name of the row this stands in, which no ellipsis cuts; nothing outside such a row or on a card wide enough for the columns. */
29
24
  export function TableCompactNameFacts() {
@@ -186,7 +186,7 @@ export const USAGE = [
186
186
  summary: "Entity rows: objects a person opens or acts on, one line each on one resting card: the identity mark and name over what it is, a few facts, a status only for the exception, when it happened, at most two row commands, and the three dots last.",
187
187
  when: "“Use `<Entity>` for a row of descriptive content paired with one or two controls (member rows, integration rows, domain rows).” (https://vercel.com/geist/entity) Ours: the records are things a person opens or acts on and that name themselves: projects, deployments, domains, members, integrations, Agents, Skills, mailboxes. A list inside another page: an Overview's exceptions, an object page's \"Needs attention\". A page whose whole job is the list is a `CollectionPage`, which draws this. Values edited in place (a domain, an environment variable) are the same rows with an `editor`: the row expands into its form. Of the three kinds of list, entity rows and rows edited in place are this; records compared across columns are a `DataTable`.",
188
188
  instead: "“For tabular data with sortable columns and shared row shape, use `Table` instead.” (https://vercel.com/geist/entity) “For a static key/value metadata block on a detail page, use `Description`.” (https://vercel.com/geist/entity) Ours: Geist's `Table` is a `DataTable`: this list has no visible headings and at most three facts, so a fourth fact is a table, and anything more than a few facts lives on the record's page. A screen gives `RecordList` the records and what each one says, never its own toolbar, table, rows or menus, and nothing else draws a record row. Its `Description` is `ObjectFacts`. Events in time are an `ActivityFeed`. Reusable things a person adds from a catalogue are a `LibraryShelf`. A descriptive row with prose is an `Item`.",
189
- behaviour: "One bordered card, a 1 px rule between records and none after the last. A row is 72 px, with 16 px above and below, and its columns stand left to right at the widths the list declares, so a person reads every list the same way: the `select` box (40 px, only when the list is picked from, an optional column), the identity (the rest: the one column without a size), the facts (each its `size`), the status (`status.size`), the time (`time.size`, tabular), the `checklist` ring (52 px, only when the record has steps to be ready) and the commands (`commandsSize`; left out, 64 px for the three dots alone). A `checklist` puts the record's `ProgressChecklist` ring just before its commands, where Vercel ends a project's row with its Production Checklist; the ring opens its own drawer, never the row. Every row has its 32 px mark, its name (600) and, under it, one muted 14 px line; `name` and the mark are required; a record with nothing to say under its name leaves the line out and is the name alone, never a dash. Give every column a `size` in pixels; the name takes the rest. With `layout=\"line\"`, as on Vercel's Projects page, the facts and the status share one muted line, joined by a middle dot, in a column as wide as the name's, and the row stays one line (mark, name over its line, that summary, the time, the ring, the three dots) on a card from 448 px, and on a narrower card the summary and the time move under the name as one muted line joined by a middle dot, in the same row, their labels read by assistive technology only. Rows of `columns` follow the same rule once the card is narrower than their columns: the facts and the time leave the row for that one line, ending in an ellipsis, never a labelled list, and the status stands beside the name, which ends in an ellipsis before it, so the exception is never the part a phone's line cuts. `facts` takes at most three. A menu of one command that is not destructive, with no row command, is that command's button, except in a `line` row, which keeps it in the three dots as Vercel's project row does; without a `commandsSize` the column is as wide as the widest such button, so its label is never cut. Status is only for the exception, empty for the usual record, and its column stands only while a record in the list shows one, so the name and its line keep the room; the commands column likewise stands only while a record has a command, a menu or a message, or the rows open, and it widens for a failed action's next steps only while a row shows one. Never the three dots before the button, a decorative icon for the record's mark, a status on every row, or a destructive button on every row: a destructive command stays in the three dots. A `line` row's summary joins only the facts the record has, so it never ends in a dot. A failed or refused action of the record shows as its catalogue message inside its own row, never a notice under the row: it replaces the detail line, with its next steps beside its commands, or under the message on a card too narrow for the row's columns. With `open` the whole row is one target and takes the `accent` hover, and a row that only opens ends in a muted chevron; a row that opens nothing never reacts to hover. With `editor` the record's row opens its form under its line. `empty` is required: `null` is the choice to draw nothing for a list with no records, and any other value is the state it draws. A note about the whole list is `footer.note`: one muted line, an optional *Learn more* after it at the left and one secondary action at the right, in a strip of its own under the count and the paging, as Vercel ends its deployments list (\"Deployment retention is enabled. Learn more\", *View recently deleted*). Colour is neutral, the name in ink and every other cell in the body grey, except the status badge and a failed action's message. “Pick the variant by what the user needs: no-results for a filtered list that returned zero rows, blank slate or informational for a resource the user hasn’t created, cleared for completed work, permission for role or tier denials, error for a failed load.” (https://vercel.com/geist/empty-state) Ours: `failed`, `noMatch` and `empty` are those variants, drawn inside the card. “The right column holds at most one or two controls. If the row needs more, move secondary actions into a `Dots Menu`.” (https://vercel.com/geist/entity) Ours: one or two row commands (outline buttons of 32 px) stand before the three dots (36 px), the Dots Menu. “Render the Skeleton variant (`entity-with-skeleton`) during load instead of an empty row, and swap to real content once data resolves.” (https://vercel.com/geist/entity) Ours: three still skeleton rows hold the place, in a region that announces what replaces them; then the first that holds: the failed read, no match, the empty state, the rows.",
189
+ behaviour: "One bordered card, a 1 px rule between records and none after the last. A row is 72 px, with 16 px above and below, and its columns stand left to right at the widths the list declares, so a person reads every list the same way: the `select` box (40 px, only when the list is picked from, an optional column), the identity (the rest: the one column without a size), the facts (each its `size`), the status (`status.size`), the time (`time.size`, tabular), the `checklist` ring (52 px, only when the record has steps to be ready) and the commands (`commandsSize`; left out, 64 px for the three dots alone). A `checklist` puts the record's `ProgressChecklist` ring just before its commands, where Vercel ends a project's row with its Production Checklist; the ring opens its own drawer, never the row. Every row has its 32 px mark, its name (600) and, under it, one muted 14 px line; `name` and the mark are required; a record with nothing to say under its name leaves the line out and is the name alone, never a dash. Give every column a `size` in pixels; the name takes the rest. With `layout=\"line\"`, as on Vercel's Projects page, the facts and the status share one muted line, joined by a middle dot, in a column as wide as the name's, and the row stays one line (mark, name over its line, that summary, the time, the ring, the three dots) on a card from 448 px, and on a narrower card the summary and the time move under the name as muted text joined by a middle dot that wraps, in the same row, their labels read by assistive technology only. Rows of `columns` follow the same rule once the card is narrower than their columns: the facts and the time leave the row for that muted text, wrapping so every value stays readable, never a labelled list, and the status stands beside the name, which ends in an ellipsis before it, so the exception is never the part a phone's line cuts. `facts` takes at most three. A menu of one command that is not destructive, with no row command, is that command's button, except in a `line` row, which keeps it in the three dots as Vercel's project row does; without a `commandsSize` the column is as wide as the widest such button, so its label is never cut. Status is only for the exception, empty for the usual record, and its column stands only while a record in the list shows one, so the name and its line keep the room; the commands column likewise stands only while a record has a command, a menu or a message, or the rows open, and it widens for a failed action's next steps only while a row shows one. Never the three dots before the button, a decorative icon for the record's mark, a status on every row, or a destructive button on every row: a destructive command stays in the three dots. A `line` row's summary joins only the facts the record has, so it never ends in a dot. A failed or refused action of the record shows as its catalogue message inside its own row, never a notice under the row: it replaces the detail line, with its next steps beside its commands, or under the message on a card too narrow for the row's columns. With `open` the whole row is one target and takes the `accent` hover, and a row that only opens ends in a muted chevron; a row that opens nothing never reacts to hover. With `editor` the record's row opens its form under its line. `empty` is required: `null` is the choice to draw nothing for a list with no records, and any other value is the state it draws. A note about the whole list is `footer.note`: one muted line, an optional *Learn more* after it at the left and one secondary action at the right, in a strip of its own under the count and the paging, as Vercel ends its deployments list (\"Deployment retention is enabled. Learn more\", *View recently deleted*). Colour is neutral, the name in ink and every other cell in the body grey, except the status badge and a failed action's message. “Pick the variant by what the user needs: no-results for a filtered list that returned zero rows, blank slate or informational for a resource the user hasn’t created, cleared for completed work, permission for role or tier denials, error for a failed load.” (https://vercel.com/geist/empty-state) Ours: `failed`, `noMatch` and `empty` are those variants, drawn inside the card. “The right column holds at most one or two controls. If the row needs more, move secondary actions into a `Dots Menu`.” (https://vercel.com/geist/entity) Ours: one or two row commands (outline buttons of 32 px) stand before the three dots (36 px), the Dots Menu. “Render the Skeleton variant (`entity-with-skeleton`) during load instead of an empty row, and swap to real content once data resolves.” (https://vercel.com/geist/entity) Ours: three still skeleton rows hold the place, in a region that announces what replaces them; then the first that holds: the failed read, no match, the empty state, the rows.",
190
190
  words: '“Lead the left column with a scannable identifier: an `Avatar` or icon, a Title Case label, then sentence-case secondary metadata (`Member since Mar 14, 2026`).” (https://vercel.com/geist/entity) Ours: the name is the record\'s own and the labels around it are sentence case, not Title Case. “Keep right-column buttons Verb + Noun (`Remove Member`, `Resend Invite`). Bare verbs like `Remove` or `Confirm` lose context once the row scrolls offscreen.” (https://vercel.com/geist/entity) Ours: every string is the product\'s: `label` names the list, a time\'s `phrase` writes its sentence ("Updated {when}"), `missing` is what a screen reader hears for a record with no time, the menu\'s `label` reads "More for {record}". A row command names its result in sentence case ("Edit domain"). A status is the exception\'s word ("Needs attention"), never "Active" on every row.',
191
191
  access: '“Wrap the loading region in `aria-busy="true"` and announce completion with `aria-live="polite"` on the destination container, not the skeleton itself.” (https://vercel.com/geist/skeleton) Ours: the loading rows sit in one `SkeletonRegion` that is busy while they stand and announces what replaces them. A table named by `label`, its column headings read by assistive technology only. The row link carries `open.label`; the three dots carry the menu\'s `label`, and the menu\'s first line names the record. The loading rows are a status named by `loadingLabel`, inside the one live region that announces what replaces them. “For multi-select rows, the leading `Checkbox` carries `aria-label="Select {entity name}"` so the row is selectable without relying on the visual label.” (https://vercel.com/geist/entity) Ours: each box is named by `select.label`, which reads "Select {record}".',
192
192
  },
@@ -780,7 +780,7 @@ export const USAGE = [
780
780
  summary: 'A record\'s identity in a row (DESIGN.md "Tables and missing values"): its mark at 32, its name, and under it one muted `detail` line saying what the record is or where it comes from, so the row reads without a column heading.',
781
781
  when: "The first cell of every collection row: the mark, the name, and one muted line saying what the record is or where it comes from, so the row reads without a heading.",
782
782
  instead: "An Agent in a gallery is `AgentListRow`; a model is `ModelIdentity`. A descriptive item with prose and actions is `Item`.",
783
- behaviour: "The mark stands 16 px before the name. The name is semibold (600) ink and the detail a muted 14 px line, two lines with no gap between them, so the row is an entity row; `mark` is required; a record with nothing to say under its name leaves `detail` out and is the name alone, never a dash, which is a table cell's missing value and not a record's. The name holds one line and ends in an ellipsis, whole in its title, with a `hint` such as an ID read after it. The detail is one line too and ends in an ellipsis, whole in its title when it is text; in a row on a card narrower than its columns, the facts that leave the row join it after a middle dot, so the record stays the name over one line, and a status that leaves the row stands beside the name, which ends in an ellipsis before it. `wraps` only when the name is the whole record, such as a memory, and then the detail wraps too. The table owns the link.",
783
+ behaviour: "The mark stands 16 px before the name. The name is semibold (600) ink and the detail a muted 14 px line, two lines with no gap between them, so the row is an entity row; `mark` is required; a record with nothing to say under its name leaves `detail` out and is the name alone, never a dash, which is a table cell's missing value and not a record's. The name holds one line and ends in an ellipsis, whole in its title, with a `hint` such as an ID read after it. The detail is one line too and ends in an ellipsis, whole in its title when it is text; in a row on a card narrower than its columns, the facts that leave the row join it after a middle dot and wrap so every value stays readable, and a status that leaves the row stands beside the name, which ends in an ellipsis before it. `wraps` only when the name is the whole record, such as a memory, and then the detail wraps too. The table owns the link.",
784
784
  words: '“Lead the left column with a scannable identifier: an `Avatar` or icon, a Title Case label, then sentence-case secondary metadata (`Member since Mar 14, 2026`).” (https://vercel.com/geist/entity) Ours: the name as the record calls itself, and the detail in sentence case: kind, then relation, three to six words, "Company skill · every Agent has it".',
785
785
  access: "The name is the row link's text; the mark is decorative; the title carries the full name.",
786
786
  },
@@ -868,7 +868,7 @@ export const USAGE = [
868
868
  summary: "Says which part of the account a page reads, and changes it.",
869
869
  when: "Changing which part of the account a page reads, such as Analytics' scope, or switching between objects from the shell's top bar with `variant=\"navigation\"`.",
870
870
  instead: "A viewer with one scope and nothing to create reads its name with no control, which this draws by itself. Finding anything is `CommandDialog`; narrowing rows is `CollectionFilters`.",
871
- behaviour: "A ghost trigger, at most 16rem, opens a 288 px popover with focus in its search. Navigation omits the visible prefix, carries each option's identity mark and opens a 384 px popover constrained to the viewport. Type to filter, Enter chooses and closes; choosing the current one changes nothing. The caller writes the choice to the URL in `onValueChange`, so sibling pages keep it. An optional create command sits last.",
871
+ behaviour: "A ghost trigger, at most 16rem, opens a 288 px popover with focus in its search. Navigation omits the visible prefix, wraps the selected name on phones instead of cutting it, carries each option's identity mark and opens a 384 px popover constrained to the viewport. Type to filter, Enter chooses and closes; choosing the current one changes nothing. The caller writes the choice to the URL in `onValueChange`, so sibling pages keep it. An optional create command sits last.",
872
872
  words: '`copy.label` names what is chosen ("Scope") beside the current scope\'s name. Search and empty lines name the thing: "Find a scope", "No scope matches".',
873
873
  access: 'The trigger reads "Scope: All customers"; the popover has a hidden title; options are a listbox with the current one checked.',
874
874
  },
@@ -1022,7 +1022,7 @@ export const USAGE = [
1022
1022
  summary: "Records compared across columns: the same facts for every record, read down a column and across a row, inside their `TableCard`.",
1023
1023
  when: "“Use `<Table>` for tabular data where rows share the same shape and at least one column is sortable or comparable across rows.” (https://vercel.com/geist/table) Ours: the person compares: usage per Agent, statements, invoices, customers with their partner and count. The columns are fixed, each has a heading, and at least one is a figure or can be sorted. There are three kinds of list. Objects a person opens (a project, a deployment, a member, an integration) are entity rows: a `RecordList`. Values edited in place (a domain, an environment variable) are entity rows that expand into their form: a `RecordList`'s `editor`. Records compared across columns are this table.",
1024
1024
  instead: "“For a row of descriptive content paired with a single action (membership row, integration row), use `Entity` instead.” (https://vercel.com/geist/table) “For a key/value metadata block on a detail page, use `Description`, not a two-column table.” (https://vercel.com/geist/table) Ours: Geist's `Entity` is a `RecordList` row. A registry of entities whose columns `RecordList` cannot hold, more than three facts beside a filter bar the product owns, as Vercel's Deployments list, is this table with `rows=\"entities\"`, which draws the same rows: headings for assistive technology only, 72 px rows, a `RecordIdentity` in the identity column. Its `Description` is `ObjectFacts`. A list on a page of its own is a `CollectionPage`. A descriptive list is `ItemGroup`.",
1025
- behaviour: "Every column has a `header`; a column without one does not compile. The headings are always visible, medium weight in the cells' body grey, 48 px high, with 12 px of room under the labels and then the rule, so the first row starts at the rule like every other; rows are 40 px. Each column is as wide as its definition's `size`, in pixels, and the columns without one share the rest, so a table gives a size to every column but the one that takes the spare width; a value too long for its column ends in an ellipsis, whole in its title (a record's name carries its own), unless the column `wraps`; a column marked `identifier` holds ids or paths and is cut in the middle instead. The last column trails unless it `wraps` or is `centered`; a `centered` column's heading and values sit in the middle of it. Records are divided by a 1 px rule, none after the last. The first and last columns stand in from the card's edges by the card's spacing, so the first lines up with the title. Records of several kinds stand in one table under `group` headings (Vercel's Usage page: *Delivery network*, *Functions*): a heading row that spans the columns, in ink, medium text, 36 px high with the same rule under it, one per group in the order the groups first appear, never a second table; a heading is the group's name and carries no figure of its own. A row that opens a record takes `accent` on hover, and any row on focus within and while its menu is open; a row that opens nothing never reacts to hover. A selected row takes `muted`; headers and footers never highlight and carry no fill. A row never scrolls sideways: on a card narrower than its columns (the sized ones and 160 px for each without a size) the row keeps its name and its `primaryColumns`, and every other column leaves it, its value joining the one muted line under the name, joined by a middle dot and ending in an ellipsis (a `Tag` reads as its text there, and a `wraps` column's prose wraps the line whole), or `dropped` when its `compact` says so, or beside an entity row's name, which ends in an ellipsis before it, when it says `name`, as Vercel's lists read on a phone: no labels, no second line; an `optional` column's empty value is left out of the line. A table whose every column is in `primaryColumns`, a settings matrix, lays out by its content there instead, so the name wraps beside the columns. A document's table, such as a legal page's, is read whole instead: its columns take the `wraps` option, so their words wrap and the row grows, and it keeps its header row; in the compact layout every cell but the identity wraps between words. A bounded page's paging stands in the card's footer strip at its trailing edge, opposite `footer`, which stands at the leading edge: its `status` (\"Page 2 of 7\") before Previous and Next, which are the product's router links (`previousLink`, `nextLink`) when each page is its own address, and buttons otherwise; the one that leads nowhere stays, disabled. A note about the whole table is `footerNote`, in a strip of its own under that one: one muted line, an optional *Learn more* after it at the left and one secondary action at the right (Vercel's deployments list ends \"Deployment retention is enabled. Learn more\" with *View recently deleted*); it is the one place a table says a sentence. Colour is neutral: headings and cells in the body grey, no hue, except a status badge or a tone the cell itself carries. “When the underlying list is empty (filter cleared, never created), render `Empty State` outside the table rather than an empty `<Table.Body>`.” (https://vercel.com/geist/table) “After an async filter change, wrap the region in `aria-live=\"polite\"` so screen readers announce the new state.” (https://vercel.com/geist/empty-state) Ours: empty sits outside the body, in a polite live region that stays in place so a filter that empties the table is announced. “Sortable column headers are buttons. The visible label stays Title Case; the sort-direction arrow is decorative and the button announces the next sort state to assistive tech.” (https://vercel.com/geist/table) Ours: the order has one owner, the app's `sorting`, which the URL holds, so no heading is a button and every page of a paged list follows one order. “Apply `tabular-nums` (or Geist Mono) to numeric columns so digits align across rows for comparison.” (https://vercel.com/geist/table) Ours: a numeric column trails in tabular figures.",
1025
+ behaviour: "Every column has a `header`; a column without one does not compile. The headings are always visible, medium weight in the cells' body grey, 48 px high, with 12 px of room under the labels and then the rule, so the first row starts at the rule like every other; rows are 40 px. Each column is as wide as its definition's `size`, in pixels, and the columns without one share the rest, so a table gives a size to every column but the one that takes the spare width; a value too long for its column ends in an ellipsis, whole in its title (a record's name carries its own), unless the column `wraps`; a column marked `identifier` holds ids or paths and is cut in the middle instead. The last column trails unless it `wraps` or is `centered`; a `centered` column's heading and values sit in the middle of it. Records are divided by a 1 px rule, none after the last. The first and last columns stand in from the card's edges by the card's spacing, so the first lines up with the title. Records of several kinds stand in one table under `group` headings (Vercel's Usage page: *Delivery network*, *Functions*): a heading row that spans the columns, in ink, medium text, 36 px high with the same rule under it, one per group in the order the groups first appear, never a second table; a heading is the group's name and carries no figure of its own. A row that opens a record takes `accent` on hover, and any row on focus within and while its menu is open; a row that opens nothing never reacts to hover. A selected row takes `muted`; headers and footers never highlight and carry no fill. A row never scrolls sideways: on a card narrower than its columns (the sized ones and 160 px for each without a size) the row keeps its name and its `primaryColumns`, and every other column leaves it, its value joining the one muted line under the name, joined by a middle dot (a `Tag` reads as its text there); entity facts wrap below the name so every value stays readable, while other rows end in an ellipsis unless a column `wraps`, or `dropped` when its `compact` says so, or beside an entity row's name, which ends in an ellipsis before it, when it says `name`, as Vercel's lists read on a phone: no labels, no second line; an `optional` column's empty value is left out of the line. A table whose every column is in `primaryColumns`, a settings matrix, lays out by its content there instead, so the name wraps beside the columns. A document's table, such as a legal page's, is read whole instead: its columns take the `wraps` option, so their words wrap and the row grows, and it keeps its header row; in the compact layout every cell but the identity wraps between words. A bounded page's paging stands in the card's footer strip at its trailing edge, opposite `footer`, which stands at the leading edge: its `status` (\"Page 2 of 7\") before Previous and Next, which are the product's router links (`previousLink`, `nextLink`) when each page is its own address, and buttons otherwise; the one that leads nowhere stays, disabled. A note about the whole table is `footerNote`, in a strip of its own under that one: one muted line, an optional *Learn more* after it at the left and one secondary action at the right (Vercel's deployments list ends \"Deployment retention is enabled. Learn more\" with *View recently deleted*); it is the one place a table says a sentence. Colour is neutral: headings and cells in the body grey, no hue, except a status badge or a tone the cell itself carries. “When the underlying list is empty (filter cleared, never created), render `Empty State` outside the table rather than an empty `<Table.Body>`.” (https://vercel.com/geist/table) “After an async filter change, wrap the region in `aria-live=\"polite\"` so screen readers announce the new state.” (https://vercel.com/geist/empty-state) Ours: empty sits outside the body, in a polite live region that stays in place so a filter that empties the table is announced. “Sortable column headers are buttons. The visible label stays Title Case; the sort-direction arrow is decorative and the button announces the next sort state to assistive tech.” (https://vercel.com/geist/table) Ours: the order has one owner, the app's `sorting`, which the URL holds, so no heading is a button and every page of a paged list follows one order. “Apply `tabular-nums` (or Geist Mono) to numeric columns so digits align across rows for comparison.” (https://vercel.com/geist/table) Ours: a numeric column trails in tabular figures.",
1026
1026
  words: '“Column headers (`<Table.Head>`) are Title Case nouns or noun phrases: `Last Used`, `Requests (7d)`, `Created`, `Status`. Never sentences.” (https://vercel.com/geist/table) “Render `—` in cells where a value is unknown or not applicable. Don’t substitute `N/A`, `null`, or an empty string.” (https://vercel.com/geist/table) “Use the canonical short relative-time form in cells (`2m ago`, `5h ago`); switch to `Mar 14, 2026` past 7 days. See `Relative Time Card`.” (https://vercel.com/geist/table) “Pagination labels are `Previous` and `Next`. Page-count copy reads `Page 2 of 7` or `21–40 of 142` with an en-dash inside the range.” (https://vercel.com/geist/table) Ours: headings are sentence case nouns with their unit, "Cost (€)", "Tokens", where Geist writes Title Case. The words of a time and of Previous and Next are the product\'s catalogue, in both languages. The footer is a count or the paging; totals and paging each stand in a footer zone of their own, the paging at the trailing edge; the summary row is a labelled total. Italian headings run a third longer and never wrap.',
1027
1027
  access: 'A native table named by `tableLabel`, which is required; `th scope="col"`; the record\'s name is the row\'s one link. A sorted column\'s heading carries `aria-sort`. Ours: a missing value is a hidden "—" with screen-reader text, never blank.',
1028
1028
  },
@@ -1198,7 +1198,7 @@ export const USAGE = [
1198
1198
  summary: "The native table every comparison is drawn with: a head row, figure rows of one line, and cells padded the same at every width.",
1199
1199
  when: "“Use `<Table>` for tabular data where rows share the same shape and at least one column is sortable or comparable across rows.” (https://vercel.com/geist/table) Ours: a fixed table a composition draws itself: a summary inside a card, a list of plan limits, figures compared down a column. A table of records is `DataTable`, which draws this one, inside a `TableCard`. It does not virtualize its rows; that is a separate capability.",
1200
1200
  instead: "“For a row of descriptive content paired with a single action (membership row, integration row), use `Entity` instead.” (https://vercel.com/geist/table) “For a key/value metadata block on a detail page, use `Description`, not a two-column table.” (https://vercel.com/geist/table) “When the underlying list is empty (filter cleared, never created), render `Empty State` outside the table rather than an empty `<Table.Body>`.” (https://vercel.com/geist/table) Ours: Geist's `Entity` is `Item`, or `RecordList` when the row names a record; its `Description` is `ObjectFacts`; its `Empty State` is `EmptyState`. Records with sorting, paging and an empty state are `DataTable`.",
1201
- behaviour: 'A head row 48 px high, with 12 px of room under its labels and then a 1 px rule, so the first row starts at the rule like every other; body rows 40 px (a cell that holds a control, such as the three dots, is padded just enough to keep the row at 40), every cell padded 8 px sideways and 10 px above and below in the body, and headings at 14 px / 20 px, weight 500, visible because people compare by them. Body rows have no rule between them unless the body is `bordered`, which draws it straight across the row. In a body that is not `bordered`, the first and last cell of a row take a 6 px corner, so a stripe or a highlight reads as a rounded bar; a `bordered` row keeps square corners, so its rule meets the edge. The last column, its heading and its cells, aligns to the trailing edge; any other numeric column takes `text-right` and `tabular-nums` to match. `striped`, `bordered` and `interactive` belong to `TableBody`: stripes fill the odd rows with `muted` (a selected row there takes `accent`), `bordered` draws a rule between rows (not after the last) and no frame, and an interactive row takes `accent` on hover, on focus within and while its menu is open, over a stripe; a row that opens nothing is not `interactive`. Headers and footers never highlight and carry no fill, and a selected row (`data-state="selected"`) takes `muted`; a footer has a rule above it and ink, medium text. A column\'s width is a `TableCol` in a `TableColgroup`. The table has no fill or edge of its own: it takes its enclosing surface, whose `Card` owns the outer edge, and a visible header and every record but the last end in a quiet 1 px `border` rule. A table that opens nothing implies no action on hover. `density="comfortable"` is the entity row of a record that names itself. On a card narrower than its `compactDetails` layout a row never scrolls sideways: its secondary columns leave the row and their values stand in the `TableDetailRow` under it, one muted line joined by a middle dot that ends in an ellipsis, their labels read by assistive technology only, as Vercel\'s lists read on a phone; a `Tag` there reads as its plain text, and a fact that `wraps` lets the line wrap whole instead of ending in an ellipsis. An entity row (`density="comfortable"`) keeps its record on one 72 px row instead: its facts join the line under the name that `RecordIdentity` draws (`TableCompactLine`), so the row is the name over one line. Colour is neutral throughout: headings and cells share the body grey and a heading differs by weight, the footer is ink, and the hue of a state lives in the `StatusBadge` of a cell, never in the row. “Sortable column headers are buttons. The visible label stays Title Case; the sort-direction arrow is decorative and the button announces the next sort state to assistive tech.” (https://vercel.com/geist/table) Ours: no heading sorts the rows, so no heading is a button; the order is the page\'s, through a `CollectionSort` above the list, and only `aria-sort` marks the column it follows. “Apply `tabular-nums` (or Geist Mono) to numeric columns so digits align across rows for comparison.” (https://vercel.com/geist/table) Ours: `DataTable` sets it on a numeric column; in a `Table` a figure\'s cell takes `tabular-nums`.',
1201
+ behaviour: 'A head row 48 px high, with 12 px of room under its labels and then a 1 px rule, so the first row starts at the rule like every other; body rows 40 px (a cell that holds a control, such as the three dots, is padded just enough to keep the row at 40), every cell padded 8 px sideways and 10 px above and below in the body, and headings at 14 px / 20 px, weight 500, visible because people compare by them. Body rows have no rule between them unless the body is `bordered`, which draws it straight across the row. In a body that is not `bordered`, the first and last cell of a row take a 6 px corner, so a stripe or a highlight reads as a rounded bar; a `bordered` row keeps square corners, so its rule meets the edge. The last column, its heading and its cells, aligns to the trailing edge; any other numeric column takes `text-right` and `tabular-nums` to match. `striped`, `bordered` and `interactive` belong to `TableBody`: stripes fill the odd rows with `muted` (a selected row there takes `accent`), `bordered` draws a rule between rows (not after the last) and no frame, and an interactive row takes `accent` on hover, on focus within and while its menu is open, over a stripe; a row that opens nothing is not `interactive`. Headers and footers never highlight and carry no fill, and a selected row (`data-state="selected"`) takes `muted`; a footer has a rule above it and ink, medium text. A column\'s width is a `TableCol` in a `TableColgroup`. The table has no fill or edge of its own: it takes its enclosing surface, whose `Card` owns the outer edge, and a visible header and every record but the last end in a quiet 1 px `border` rule. A table that opens nothing implies no action on hover. `density="comfortable"` is the entity row of a record that names itself. On a card narrower than its `compactDetails` layout a row never scrolls sideways: its secondary columns leave the row and their values stand in the `TableDetailRow` under it, one muted line joined by a middle dot that ends in an ellipsis, their labels read by assistive technology only, as Vercel\'s lists read on a phone; a `Tag` there reads as its plain text, and a fact that `wraps` lets the line wrap whole instead of ending in an ellipsis. An entity row (`density="comfortable"`) keeps its facts in the identity cell instead: they join the text under the name that `RecordIdentity` draws (`TableCompactLine`), and wrap so every value stays readable. Colour is neutral throughout: headings and cells share the body grey and a heading differs by weight, the footer is ink, and the hue of a state lives in the `StatusBadge` of a cell, never in the row. “Sortable column headers are buttons. The visible label stays Title Case; the sort-direction arrow is decorative and the button announces the next sort state to assistive tech.” (https://vercel.com/geist/table) Ours: no heading sorts the rows, so no heading is a button; the order is the page\'s, through a `CollectionSort` above the list, and only `aria-sort` marks the column it follows. “Apply `tabular-nums` (or Geist Mono) to numeric columns so digits align across rows for comparison.” (https://vercel.com/geist/table) Ours: `DataTable` sets it on a numeric column; in a `Table` a figure\'s cell takes `tabular-nums`.',
1202
1202
  words: "“Column headers (`<Table.Head>`) are Title Case nouns or noun phrases: `Last Used`, `Requests (7d)`, `Created`, `Status`. Never sentences.” (https://vercel.com/geist/table) “Render `—` in cells where a value is unknown or not applicable. Don’t substitute `N/A`, `null`, or an empty string.” (https://vercel.com/geist/table) “Use the canonical short relative-time form in cells (`2m ago`, `5h ago`); switch to `Mar 14, 2026` past 7 days. See `Relative Time Card`.” (https://vercel.com/geist/table) Ours: headings are sentence case nouns with their unit, *Cost (€)*, *Tokens*, where Geist writes Title Case. Italian headings run a third longer and never wrap. A missing value is `TableValue`'s dash, read as its `missingLabel`. The table is always named: its `aria-label` says what it lists (*Usage by Agent*).",
1203
1203
  access: 'A native `table` named by `aria-label` or `aria-labelledby`, which the types require, or `aria-hidden` when it is only the placeholder of a loading view; `th scope="col"`, with `aria-sort` on the column the rows are ordered by.',
1204
1204
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nadicodeai/ui",
3
- "version": "12.1.134",
3
+ "version": "12.1.135",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -83,7 +83,7 @@
83
83
  "@base-ui/react": "^1.9.0",
84
84
  "@iconify-icons/logos": "^2.0.3",
85
85
  "@iconify/react": "^6.0.2",
86
- "@nadicodeai/design-system": "12.1.134",
86
+ "@nadicodeai/design-system": "12.1.135",
87
87
  "@tanstack/react-table": "^9.3.1",
88
88
  "@vgpu/wgsl": "0.6.0",
89
89
  "class-variance-authority": "^0.7.1",