@nadicodeai/ui 12.1.128 → 12.1.130

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/components/agent-list-row.d.ts +1 -1
  2. package/dist/components/agent-list-row.js +1 -1
  3. package/dist/components/badge.d.ts +7 -6
  4. package/dist/components/badge.d.ts.map +1 -1
  5. package/dist/components/badge.js +7 -6
  6. package/dist/components/data-table.d.ts +5 -3
  7. package/dist/components/data-table.d.ts.map +1 -1
  8. package/dist/components/data-table.js +2 -1
  9. package/dist/components/identity-mark.d.ts +10 -5
  10. package/dist/components/identity-mark.d.ts.map +1 -1
  11. package/dist/components/identity-mark.js +9 -5
  12. package/dist/components/record-identity.d.ts +1 -1
  13. package/dist/components/record-identity.d.ts.map +1 -1
  14. package/dist/components/record-identity.js +3 -3
  15. package/dist/components/record-list.d.ts +1 -1
  16. package/dist/components/record-list.js +2 -2
  17. package/dist/components/settings-section.d.ts +1 -1
  18. package/dist/components/settings-section.d.ts.map +1 -1
  19. package/dist/components/settings-section.js +7 -3
  20. package/dist/components/status-badge.d.ts +20 -25
  21. package/dist/components/status-badge.d.ts.map +1 -1
  22. package/dist/components/status-badge.js +40 -62
  23. package/dist/internal/status-mark.d.ts +16 -0
  24. package/dist/internal/status-mark.d.ts.map +1 -0
  25. package/dist/internal/status-mark.js +128 -0
  26. package/dist/internal/table-compact-line.d.ts +4 -0
  27. package/dist/internal/table-compact-line.d.ts.map +1 -1
  28. package/dist/internal/table-compact-line.js +15 -2
  29. package/dist/showcase/usage.generated.js +16 -16
  30. package/dist/styles/components.css +1 -0
  31. package/package.json +2 -2
@@ -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, the status and the time leave the row for that one line, ending in an ellipsis, never a labelled list. `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 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.",
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
  },
@@ -230,7 +230,7 @@ export const USAGE = [
230
230
  summary: "A card of one setting that says what changing it does and ends in a footer.",
231
231
  when: "One setting a person changes on a SettingsPage or as a section of an ObjectPage: Personal details, Company identity, Notifications. A typed value takes `save`; a switch or a short reversible choice applies at once through its own control's change, with no Save. Also a plan-gated feature, a delete card, one read-only value (an ID in a read-only mono InputGroup with its copy button, no Save), and a paid quantity (its stepper field, the price as the description, a two-fact summary under it).",
232
232
  instead: "Read-only facts of an object are a SettingsFactsSection; a table or a list of records is a TableCard; Theme and Language live in the account menu; a record's edit in a list is a RowEditForm.",
233
- behaviour: '"Groups related form controls inside a bordered card with optional footer actions." (https://vercel.com/geist/fieldset) Ours: one setting per card: a single SettingsField, the description its only help. A typed value (a name, a URL, a server, a port, a key) gets its own card. A semantic subgroup of related controls inside a field, such as radios or checkboxes, is a `FieldSet` with a `FieldLegend`. One more shape exists, declared by a part and never by classes at the call site. A card of several settings holds SettingsRows, label left and control right, only when every row is a switch or every row is a select. "Use Toggle for a single boolean setting where ON takes effect immediately, like `Password Protection` or `Auto-Cancel Builds`." (https://vercel.com/geist/toggle) "Pair with a form footer only when the setting needs an explicit `Save` step." (https://vercel.com/geist/toggle) "Disable Toggle only when the action is impossible (missing plan, locked policy); pair with helper text or a `Tooltip` that names the resolver." (https://vercel.com/geist/toggle) Ours: a switch never applies on change; it is a row of the card, a draft until the footer\'s Save, as Vercel\'s settings cards are. A switch blocked from above is shown off and disabled, the footer saying why and never a tooltip. Required: the title and the description. The header\'s actions stand at the title\'s right; on a card narrower than 28 rem they keep the title\'s row and the description runs under both, as Vercel\'s cards read on a phone. The footer\'s left line (`footerHint`, `save.hint`) is optional and specific to the card, never a generic line repeated on every card: a limit, what a value is for, a plan it needs, a link; absent, it is nothing, and a card with neither a line nor a button has no footer. Ours: `save`: a typed value, one card with its own Save in the footer, always visible. The section owns the changed state: Save stays disabled until a field differs from what is saved and says `pendingLabel` while saving with Cancel beside it; a completed save calls `saved`, and the product raises one toast from its catalogue ("Completion toasts follow `{Noun} {past-participle}`: `Blob deleted`, `Domain added`, `Environment variable saved`. Never include `successfully`; the action name implies it." https://vercel.com/geist/toast), and Save returns to disabled, with no saved state in the footer. Ours: the section never raises a toast itself. A failed save keeps every value and shows under its field, or above the footer when no field owns it. `tone` marks a state with a command (a failed payment, a trial ending): a 1px border and a footer tinted in the hue with its line in it, the line saying the condition in words; title, line, fields and buttons stay neutral, an outline secondary then a default primary. A button takes the hue of its command, not of the card: `destructive` (red) for a permanent loss (Delete), `warning` (amber) for a disruptive command that can be undone (Pause, Archive). A delete card is `tone="error"` with the object\'s summary as its body; an archive card is neutral with a `warning` button in the footer. "Use a Note for inline contextual feedback next to the field, card, or section it describes: a region change warning above a region picker, a rate-limit notice next to a usage gauge." (https://vercel.com/geist/note) "One Note per concept." (https://vercel.com/geist/note) Ours: a message about the fields inside is a MessageNotice in the body, the card staying neutral. "Disabled checkboxes still need a Tooltip naming the reason; a greyed box with no explanation reads as a bug." (https://vercel.com/geist/checkbox) Ours: `lockedReason` (Geist\'s disabled wall) is the reason, written as the footer\'s line, not in a tooltip; it keeps the fields in view, inert with muted text, under a not-allowed cursor, the title and line unchanged; `unlock` is the way to get access, left out when nothing the person does unlocks it. A locked section has no `save` and no header `actions`.',
233
+ behaviour: '"Groups related form controls inside a bordered card with optional footer actions." (https://vercel.com/geist/fieldset) Ours: one setting per card: a single SettingsField, the description its only help. A typed value (a name, a URL, a server, a port, a key) gets its own card. A semantic subgroup of related controls inside a field, such as radios or checkboxes, is a `FieldSet` with a `FieldLegend`. One more shape exists, declared by a part and never by classes at the call site. A card of several settings holds SettingsRows, label left and control right, only when every row is a switch or every row is a select. "Use Toggle for a single boolean setting where ON takes effect immediately, like `Password Protection` or `Auto-Cancel Builds`." (https://vercel.com/geist/toggle) "Pair with a form footer only when the setting needs an explicit `Save` step." (https://vercel.com/geist/toggle) "Disable Toggle only when the action is impossible (missing plan, locked policy); pair with helper text or a `Tooltip` that names the resolver." (https://vercel.com/geist/toggle) Ours: a switch never applies on change; it is a row of the card, a draft until the footer\'s Save, as Vercel\'s settings cards are. A switch blocked from above is shown off and disabled, the footer saying why and never a tooltip. Required: the title and the description. The header\'s actions stand at the title\'s right; on a card narrower than 28 rem they keep the title\'s row and the description runs under both, as Vercel\'s cards read on a phone, and when the title\'s longest word would not fit beside them they move under the title, so they never cover it. The footer\'s left line (`footerHint`, `save.hint`) is optional and specific to the card, never a generic line repeated on every card: a limit, what a value is for, a plan it needs, a link; absent, it is nothing, and a card with neither a line nor a button has no footer. Ours: `save`: a typed value, one card with its own Save in the footer, always visible. The section owns the changed state: Save stays disabled until a field differs from what is saved and says `pendingLabel` while saving with Cancel beside it; a completed save calls `saved`, and the product raises one toast from its catalogue ("Completion toasts follow `{Noun} {past-participle}`: `Blob deleted`, `Domain added`, `Environment variable saved`. Never include `successfully`; the action name implies it." https://vercel.com/geist/toast), and Save returns to disabled, with no saved state in the footer. Ours: the section never raises a toast itself. A failed save keeps every value and shows under its field, or above the footer when no field owns it. `tone` marks a state with a command (a failed payment, a trial ending): a 1px border and a footer tinted in the hue with its line in it, the line saying the condition in words; title, line, fields and buttons stay neutral, an outline secondary then a default primary. A button takes the hue of its command, not of the card: `destructive` (red) for a permanent loss (Delete), `warning` (amber) for a disruptive command that can be undone (Pause, Archive). A delete card is `tone="error"` with the object\'s summary as its body; an archive card is neutral with a `warning` button in the footer. "Use a Note for inline contextual feedback next to the field, card, or section it describes: a region change warning above a region picker, a rate-limit notice next to a usage gauge." (https://vercel.com/geist/note) "One Note per concept." (https://vercel.com/geist/note) Ours: a message about the fields inside is a MessageNotice in the body, the card staying neutral. "Disabled checkboxes still need a Tooltip naming the reason; a greyed box with no explanation reads as a bug." (https://vercel.com/geist/checkbox) Ours: `lockedReason` (Geist\'s disabled wall) is the reason, written as the footer\'s line, not in a tooltip; it keeps the fields in view, inert with muted text, under a not-allowed cursor, the title and line unchanged; `unlock` is the way to get access, left out when nothing the person does unlocks it. A locked section has no `save` and no header `actions`.',
234
234
  words: '"Helper text is sentence case, one sentence with a trailing period, on a sibling element wired through `aria-describedby`." (https://vercel.com/geist/input) Ours: the description is that helper text for the whole card, and no field has its own. "Render an optional one-sentence description as a sibling under the label that explains ON only. Don’t describe OFF; it’s the negation." (https://vercel.com/geist/toggle) Ours: a header-switch card\'s description says what turning it on does. The title names the values ("Personal details"); the description says what changing them does, in one line; the footer line is a Learn more link, a limit ("Use 32 characters at most."), what stays unaffected, or what is blocked and why ("Ask an owner to change this."); Save and Saving come from the product\'s catalogue, and so does the toast a `saved` callback raises; a delete card\'s description opens "Permanently delete".',
235
235
  access: "\"Provide an accessible name through `children`, `aria-label`, or `aria-labelledby`.\" (https://vercel.com/geist/toggle) Ours: the title is the section's h2 and names the form that saves it, and the header switch. The footer's line is one status, announced politely; a failure under a field marks the control invalid and describes it. Locked fields are inert, so the section's title, description and reason are what a screen reader meets.",
236
236
  },
@@ -669,8 +669,8 @@ export const USAGE = [
669
669
  family: "Status",
670
670
  summary: 'Short metadata beside its subject: a role, a plan, "Beta".',
671
671
  when: '"Use Badge for short, scannable metadata that sits next to the thing it describes: status, plan tier, environment, or role." (https://vercel.com/geist/badge) "One badge per row; two side by side is a sign the row needs a second column." (https://vercel.com/geist/badge) Ours: a state is a `StatusBadge`; a `Badge` is a role (*Owner*), a plan, an environment, *Beta*. It always has a word, or one `icon` with a `title`, and the type asks for it. `size` follows the type beside it: `sm` (20 px) next to small labels, `md` (24 px) in a row, `lg` (32 px) in a header. The category fills (teal, purple, pink) name a kind.',
672
- instead: '"For a colored dot without text, use `Status Dot`." (https://vercel.com/geist/badge) Ours: status dots are not used; a state keeps its mark and word together in `StatusBadge`. "For clickable filter chips that toggle a query, use the `pill` variant or a small `Button`." (https://vercel.com/geist/badge) Ours: there is no `pill` variant; a filter that toggles a query is a `Button` or `CollectionFilters`. For a state use `StatusBadge`, which picks the tone and its mark: the gray, green, red, amber and blue fills, soft or solid, are its alone. One categorical fact in a row is a `Tag`. A change in a figure is a `TrendBadge`.',
673
- behaviour: '"Badges are static labels." (https://vercel.com/geist/badge) "Don’t wire `onClick` onto them; promote to a Button or link if the user can act on the value." (https://vercel.com/geist/badge) Ours: it takes no `onClick`, no hover and no focus; it becomes a link only through `render` with a real anchor, and only `outline` takes the hover, a gray fill. "Keep badge content to text or `icon` + text." (https://vercel.com/geist/badge) "Never stack two icons or a child Badge inside a Badge." (https://vercel.com/geist/badge) Ours: never a paragraph either. "Pair lifecycle badges (`Alpha`, `Beta`, `Early Access`) with a `Tooltip` that names the limit, like `Alpha: API may change before GA`." (https://vercel.com/geist/badge) Ours: the badge is the `render` of a `TooltipTrigger` with `tabIndex={0}`, so keyboard and touch users reach the limit it names. "Don’t add a checkmark icon for success states or an X for errors; the variant carries that signal." (https://vercel.com/geist/badge) Ours: only `StatusBadge` carries a state, and it always draws its tone\'s mark. "Map meaning to color: `green` for healthy, `red` for error, `amber` for warning, `blue` for informational or production, `gray` for neutral." (https://vercel.com/geist/badge) "The `-subtle` suffix tones any of them down on dense surfaces." (https://vercel.com/geist/badge) Ours: those fills are `StatusBadge`\'s, as the tones `success`, `danger`, `warning`, `info` and `neutral`, with `emphasis` `subtle` or `solid`. `Badge` is a fixed-height pill (20, 24 or 32 px) with no border: colour sits on the fill and the word. A soft fill takes its hue\'s strong shade for the word, `default` is the foreground colour inverted, and `outline` is neutral, a page-colour fill held by a 1 px inset ring. With an icon, the left padding is 6, 8 or 12 px, so the mark and the word look evenly spaced. The icon takes the word\'s colour.',
672
+ instead: '"For a colored dot without text, use `Status Dot`." (https://vercel.com/geist/badge) Ours: a single dot is never a status; a state is `StatusBadge`, its tone\'s shape of dots and its word, with no pill. "For clickable filter chips that toggle a query, use the `pill` variant or a small `Button`." (https://vercel.com/geist/badge) Ours: there is no `pill` variant; a filter that toggles a query is a `Button` or `CollectionFilters`. For a state use `StatusBadge`, which draws the tone as its shape; Badge has no gray, green, red, amber or blue fill. One categorical fact in a row is a `Tag`. A change in a figure is a `TrendBadge`.',
673
+ behaviour: '"Badges are static labels." (https://vercel.com/geist/badge) "Don’t wire `onClick` onto them; promote to a Button or link if the user can act on the value." (https://vercel.com/geist/badge) Ours: it takes no `onClick`, no hover and no focus; it becomes a link only through `render` with a real anchor, and only `outline` takes the hover, a gray fill. "Keep badge content to text or `icon` + text." (https://vercel.com/geist/badge) "Never stack two icons or a child Badge inside a Badge." (https://vercel.com/geist/badge) Ours: never a paragraph either. "Pair lifecycle badges (`Alpha`, `Beta`, `Early Access`) with a `Tooltip` that names the limit, like `Alpha: API may change before GA`." (https://vercel.com/geist/badge) Ours: the badge is the `render` of a `TooltipTrigger` with `tabIndex={0}`, so keyboard and touch users reach the limit it names. "Don’t add a checkmark icon for success states or an X for errors; the variant carries that signal." (https://vercel.com/geist/badge) Ours: only `StatusBadge` carries a state, and it always draws its tone\'s shape. "Map meaning to color: `green` for healthy, `red` for error, `amber` for warning, `blue` for informational or production, `gray` for neutral." (https://vercel.com/geist/badge) "The `-subtle` suffix tones any of them down on dense surfaces." (https://vercel.com/geist/badge) Ours: those meanings are `StatusBadge`\'s, as the tones `success`, `danger`, `warning`, `info` and `neutral`, each drawn as its shape. `Badge` is a fixed-height pill (20, 24 or 32 px) with no border: colour sits on the fill and the word. A soft fill takes its hue\'s strong shade for the word, `default` is the foreground colour inverted, and `outline` is neutral, a page-colour fill held by a 1 px inset ring. With an icon, the left padding is 6, 8 or 12 px, so the mark and the word look evenly spaced. The icon takes the word\'s colour.',
674
674
  words: '"Title Case, one word when possible, two max: `Active`, `Pending`, `Pro`, `Enterprise Trial`." (https://vercel.com/geist/badge) Ours: sentence case (*Enterprise trial*). "Match the canonical API or log term: `Production` not `Prod`, `Deployed` not `Live`, `Canceled` not `Cancelled` (the Vercel API uses one L)." (https://vercel.com/geist/badge) Ours: the term is our own API\'s. "Skip stuffing sentences inside (`Currently Active`, `You are on Pro`); the surrounding row supplies the context." (https://vercel.com/geist/badge)',
675
675
  access: '"Set `title` for icon-only or ambiguous badges so screen readers announce the meaning." (https://vercel.com/geist/badge) Ours: an icon alone must set `title`, and the type asks for it. Read in place as text. "Don’t rely on color alone; the text has to be readable without it." (https://vercel.com/geist/badge) Ours: a coloured variant still carries its word.',
676
676
  },
@@ -711,11 +711,11 @@ export const USAGE = [
711
711
  name: "IdentityMark",
712
712
  module: "identity-mark",
713
713
  family: "Status",
714
- summary: "What says which thing a record is, at a fixed size at the start of its row, its object's head or its feed line: an Agent's orb, an app's logo, a company's initials, a person's picture, or a record kind's icon on its tile.",
714
+ summary: "What says which thing a record is, at a fixed size at the start of its row, its object's head or its feed line: an Agent's orb, an app's logo, a model maker's mark, a company's initials, a person's picture, or a record kind's icon on its tile.",
715
715
  when: "The first thing in a record's row (32), at the head of its object page (64), in a feed or a sidebar identity line (24), on a Library entry (40), for the company at the top of Nadia's list (28): wherever it tells a person which thing they are looking at.",
716
- instead: "An icon beside a card's title, a KPI's label or a section heading only repeats the words and is left out. A picture for a first use or a whole-page moment is the empty state's or the page's own. A model is its `ModelIdentity`.",
717
- behaviour: "Static, except an Agent's orb, which moves with her state. An Agent is always her real orb from the orb engine, at the largest orb size not above the mark's, with no tile and no ring, never an avatar or a painted circle, and `frame` lets the product wrap that orb, such as in the transition that carries her to her page; an app keeps its own logo in its bordered tile on the page ground, or its first letter when it has no logo; a company is its own logo, contained whole in a neutral tile in its original colours and proportions, as Vercel shows a project's favicon; its two initials on a compact arch, cobalto for a company and arancio for an approved Partner, until a logo is found or when the stored logo does not load. The edition follows the company's standing, never the directory it was opened from; a person is their picture, then their initials, in a round mark; a record with its own object picture, such as a Library skill or plugin, draws that picture on its kind's ground; a record with no picture of its own takes its kind's own icon, the same icon its section has in the navigation (Mail for a mailbox, a server for an MCP server, a key for a password), in a ringed tile on the card ground; a Library skill takes the purple category ground with the Skills icon and a plugin the `info-soft` ground with the Plugins icon, both without a ring, so the ground says which kind it is, and an entry that has its own object picture draws it on that ground in the icon's place. A company arch has a semicircular top and `{rounded.sm}` bottom corners; a record tile takes `{rounded.sm}` (6 px) corners, `{rounded.md}` (8 px) at 40 and 64; an app's tile takes `{rounded.md}` at every size.",
718
- words: "None of its own: the record's name stands beside it. Initials stand for a company or a person: the first letters of the name's first two words. An app with no logo takes the first letter of its name as a monogram, and no other kind takes letters.",
716
+ instead: "An icon beside a card's title, a KPI's label or a section heading only repeats the words and is left out. A picture for a first use or a whole-page moment is the empty state's or the page's own. A model is its `ModelIdentity`; a maker's mark beside a model's name in a list of models is `ModelMark` at 24.",
717
+ behaviour: "Static, except an Agent's orb, which moves with her state. An Agent is always her real orb from the orb engine, at the largest orb size not above the mark's, with no tile and no ring, never an avatar or a painted circle, and `frame` lets the product wrap that orb, such as in the transition that carries her to her page; an app keeps its own logo in its bordered tile on the page ground, or its first letter when it has no logo; a record that belongs to a model maker, such as a key held at that maker, is the maker's `ModelMark` at the mark's size, with its initials when LobeHub has no mark for it; a company is its own logo, contained whole in a neutral tile in its original colours and proportions, as Vercel shows a project's favicon; its two initials on a compact arch, cobalto for a company and arancio for an approved Partner, until a logo is found or when the stored logo does not load. The edition follows the company's standing, never the directory it was opened from; a person is their picture, then their initials, in a round mark; a record with its own object picture, such as a Library skill or plugin, draws that picture on its kind's ground; a record with no picture of its own takes its kind's own icon, the same icon its section has in the navigation (Mail for a mailbox, a server for an MCP server, a key for a password), in a ringed tile on the card ground; a Library skill takes the purple category ground with the Skills icon and a plugin the `info-soft` ground with the Plugins icon, both without a ring, so the ground says which kind it is, and an entry that has its own object picture draws it on that ground in the icon's place. A company arch has a semicircular top and `{rounded.sm}` bottom corners; a record tile takes `{rounded.sm}` (6 px) corners, `{rounded.md}` (8 px) at 40 and 64; an app's tile takes `{rounded.md}` at every size.",
718
+ words: "None of its own: the record's name stands beside it. Initials stand for a company or a person: the first letters of the name's first two words. An app with no logo takes the first letter of its name as a monogram, a model maker with no mark the initials of its slug, and no other kind takes letters.",
719
719
  access: "Decorative beside the record's name. Alone, as in a narrow rail, `label` names it.",
720
720
  },
721
721
  {
@@ -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. `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, 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.",
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
  },
@@ -788,12 +788,12 @@ export const USAGE = [
788
788
  name: "StatusBadge",
789
789
  module: "status-badge",
790
790
  family: "Status",
791
- summary: "A record's state: a mark and a word in one of five tones.",
792
- when: '"Use Badge for short, scannable metadata that sits next to the thing it describes: status, plan tier, environment, or role." (https://vercel.com/geist/badge) Ours: a record\'s state, when it sets the row apart: failed, waiting on a person, a step in motion, paused, connected. Gray is the default and every terminal state that needs no action; green is healthy or complete; red is unusable or failed; amber is waiting on a person\'s decision; blue is a step in motion. "Map meaning to color: `green` for healthy, `red` for error, `amber` for warning, `blue` for informational or production, `gray` for neutral." (https://vercel.com/geist/badge) Ours: success is green, danger red, warning amber, info blue, neutral gray, whatever the state\'s name, in our colour values. The tone and the word are required, and the type asks for both.',
793
- instead: '"For non-deployment statuses (Workflow runs, Queue messages, Sandbox, Cron jobs), use a `Badge` with the canonical state vocabulary instead of repurposing the dot." (https://vercel.com/geist/status-dot) Ours: that `Badge` is `StatusBadge`. For a category (an installation, a partner) use `Tag`. For plain metadata (a role, a plan) use `Badge`. For a control reporting its own action use `ActionStatus`. A state beside its name still uses this badge; status dots are not used.',
794
- behaviour: '"Badges are static labels." (https://vercel.com/geist/badge) "Don’t wire `onClick` onto them; promote to a Button or link if the user can act on the value." (https://vercel.com/geist/badge) Ours: a status never moves and is never a button. "The `-subtle` suffix tones any of them down on dense surfaces." (https://vercel.com/geist/badge) Ours: `subtle`, the default, is for rows, tables, lists and menus: it fills the pill with the tone\'s soft ground and sets the mark and the word in its strong shade; gray takes the foreground colour, never a muted one. `solid` is for a status that stands on its own, such as an object\'s page header or a card title: it fills the pill with the tone\'s strong colour and sets the mark and the word on it. No border and no ring either way. The `subtle` pairs are the tone\'s soft ground with its deep shade (gray: `canvas-soft-2` with the foreground colour). "Don’t add a checkmark icon for success states or an X for errors; the variant carries that signal." (https://vercel.com/geist/badge) Ours: it always draws its tone\'s mark, even for success and error: the mark repeats the tone as a shape so a status reads without colour. Each app keeps one value-to-tone map in one module; nothing else picks a status colour. A healthy record shows no badge, except the operational-state and run-outcome columns defined by DESIGN.md\'s Status rule.',
791
+ summary: "A record's state: a shape of dots and a word, in one of five tones.",
792
+ when: '"Use Badge for short, scannable metadata that sits next to the thing it describes: status, plan tier, environment, or role." (https://vercel.com/geist/badge) Ours: a record\'s state, when it sets the row apart: failed, waiting on a person, a step in motion, paused, connected. Gray is the default and every terminal state that needs no action; green is healthy or complete; red is unusable or failed; amber is waiting on a person\'s decision; blue is a step in motion. Each tone has its own shape: green a circle, blue a diamond, amber a triangle, red a square, gray an empty ring. "Map meaning to color: `green` for healthy, `red` for error, `amber` for warning, `blue` for informational or production, `gray` for neutral." (https://vercel.com/geist/badge) Ours: success is green, danger red, warning amber, info blue, neutral gray, whatever the state\'s name, in our colour values. The tone and the word are required, and the type asks for both.',
793
+ instead: '"For non-deployment statuses (Workflow runs, Queue messages, Sandbox, Cron jobs), use a `Badge` with the canonical state vocabulary instead of repurposing the dot." (https://vercel.com/geist/status-dot) Ours: that `Badge` is `StatusBadge`. For a category (an installation, a partner) use `Tag`. For plain metadata (a role, a plan) use `Badge`. For a control reporting its own action use `ActionStatus`. A state beside its name still uses this component.',
794
+ behaviour: "\"Badges are static labels.\" (https://vercel.com/geist/badge) \"Don’t wire `onClick` onto them; promote to a Button or link if the user can act on the value.\" (https://vercel.com/geist/badge) Ours: a status never moves and is never a button. It is the tone's shape and the word, with no pill, no fill and no border, as Vercel's deployment list sets a state beside its time. The shape is filled with dots on a honeycomb grid, the material of the Agent orbs: an outline of evenly spaced dots and a field of dots inside it; gray's ring stays empty, the quiet tone. The dots take the tone's colour and the word the foreground colour; amber's outline takes its deep step so the triangle holds on white. `size` follows the type beside it: `sm` (a 14 px shape, 12 px word) in a small line such as an Agent's state line, `md` (16 px, 13 px) in a row, `lg` (20 px, 14 px) beside an object's name.",
795
795
  words: '"Title Case, one word when possible, two max: `Active`, `Pending`, `Pro`, `Enterprise Trial`." (https://vercel.com/geist/badge) Ours: a state, not a verb (*Failed*, *Waiting for you*), in sentence case, one to three words, no trailing ellipsis. Italian runs a third longer: *In attesa di te*. "Match the canonical API or log term: `Production` not `Prod`, `Deployed` not `Live`, `Canceled` not `Cancelled` (the Vercel API uses one L)." (https://vercel.com/geist/badge) "Skip stuffing sentences inside (`Currently Active`, `You are on Pro`); the surrounding row supplies the context." (https://vercel.com/geist/badge)',
796
- access: '"Don’t rely on color alone; the text has to be readable without it." (https://vercel.com/geist/badge) Ours: the word is the name; the mark is `aria-hidden`, so colour never carries the state alone. Not focusable.',
796
+ access: '"Don’t rely on color alone; the text has to be readable without it." (https://vercel.com/geist/badge) Ours: the word is the name; the shape is `aria-hidden`, so colour never carries the state alone. Not focusable. Each app keeps one value-to-tone map in one module; nothing else picks a status colour. A healthy record shows no status, except the operational-state and run-outcome columns defined by DESIGN.md\'s Status rule.',
797
797
  },
798
798
  {
799
799
  name: "Tag",
@@ -815,7 +815,7 @@ export const USAGE = [
815
815
  instead: "An Agent as a record among others in a page's list is a RecordList row with an agent IdentityMark; an Agent's own page heads with ObjectPage.",
816
816
  behaviour: "It is the row's presentation alone: the consumer owns the control around it (Nadia's button that opens the Agent, a scene's list item that opens nothing). The current row's name is set in 500. A state tone draws the state line as a small StatusBadge; without a tone it stays plain text. Narrow, the orb stands alone and the words stay for a screen reader; the consumer names it with its state line in a tooltip, as Nadia's list does. Only the orb moves in the list. The name is 14 px and the state line 12 px at every size. The drawn orb leaves about 10 px of its 64 px box empty, so at 64 px that empty edge overlaps the row's padding (6 px above and below, 4 px at the start) and the words stand 4 px from the box: the row hugs the visible orb and the name sits beside it, as beside a solid picture. In Nadia's list her orb is 64 px in her real state, and 40 px in the narrow column from 640 to 899 px.",
817
817
  words: 'The name, then one state line: what she is doing now ("Answering on Telegram", "Working") or what she is for ("Sorts requests"). The tone never says more than the line does. Nadia\'s list says "Working", "Idle" or "Needs you"; the badge marks a state that needs the person ("Needs you", a failed set-up), and the orb shows working by itself.',
818
- access: "The orb is decorative; the name and the state line are the row's text, read by its control. StatusBadge owns its decorative mark, so the line carries its meaning.",
818
+ access: "The orb is decorative; the name and the state line are the row's text, read by its control. StatusBadge owns its decorative shape, so the line carries its meaning.",
819
819
  },
820
820
  {
821
821
  name: "Breadcrumb",
@@ -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, 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 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.",
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
  },
@@ -171,6 +171,7 @@
171
171
  :has(
172
172
  > :only-child:is(
173
173
  [data-slot="badge"],
174
+ [data-slot="status-badge"],
174
175
  [data-slot="trend-badge"],
175
176
  [data-slot="chart-sparkline"],
176
177
  [data-slot="button"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nadicodeai/ui",
3
- "version": "12.1.128",
3
+ "version": "12.1.130",
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.128",
86
+ "@nadicodeai/design-system": "12.1.130",
87
87
  "@tanstack/react-table": "^9.3.1",
88
88
  "@vgpu/wgsl": "0.6.0",
89
89
  "class-variance-authority": "^0.7.1",