@nadicodeai/design-system 12.1.165 → 12.1.166

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.
package/AGENTS.md CHANGED
@@ -12,15 +12,15 @@ usage page in its source; `examples/` contains rendered specimens.
12
12
  - Terms: `GLOSSARY.md`. Static consumption: `README.md`.
13
13
  - React consumption: `../ui/README.md`.
14
14
  - Visual checks: `playwright.visual.config.ts` and `tests/visual/`, run by
15
- post-merge's `test (website, packages)` job. Its `-linux` baselines are what the
15
+ post-merge's `test (website)` job. Its `-linux` baselines are what the
16
16
  CI runner image (`../../tools/ci/host/Dockerfile`) renders: after an intended
17
- visual change, rerun `npm run test:visual -w @nadicodeai/design-system --
18
- --update-snapshots` inside that image and commit the new screenshots.
17
+ visual change, rerun `pnpm --filter @nadicodeai/design-system test:visual --update-snapshots`
18
+ inside that image and commit the new screenshots.
19
19
  - Releases: `../../docs/agents/publish.md`.
20
20
 
21
21
  Edit token sources (the four groups in `DESIGN.md`'s front matter, every other
22
22
  value in `src/tokens/extension.tokens.json`), then run
23
- `npm run build -w @nadicodeai/design-system`. Generated CSS and `dist/`
23
+ `pnpm --filter @nadicodeai/design-system build`. Generated CSS and `dist/`
24
24
  are outputs, not hand-edited sources. Favicon source changes use
25
25
  `generate:favicons`; a lucide-static upgrade uses `generate:icons`.
26
26
  `package.json` owns the commands; shared validation policy is in
package/DESIGN.md CHANGED
@@ -539,7 +539,7 @@ The products are assembled in three layers, each with one owner and one check, s
539
539
 
540
540
  - **Components.** Every component in `@nadicodeai/ui` has a usage page: when to use it, what to use instead, how it behaves, its words and its accessibility, written in the component's own source and rendered by the package's showcase ("Component selection and usage").
541
541
  - **Compositions.** A page is built from one of nine compositions, each a component in `@nadicodeai/ui` with fixed slots, so the page supplies only content and the order of its actions and the place of its three dots cannot drift: the five page compositions, the record row, the empty state, the overlays and the identity marks ("Page compositions").
542
- - **Checks.** Four lint rules fail the build in the Portal, Nadia's window and Alveare Server: a page renders a composition (`page-composition`), an absence is only `EmptyState` (`empty-state-owner`), commands go only through `PageActions`, `ActionMenu` and `AccountMenu` (`menu-owner`, with the menu modules held by `no-restricted-imports`), and a title or a label carries no icon (`no-decorative-icon`). A page that broke a rule when the checks arrived sits on its product's recorded baseline, whose count may only fall.
542
+ - **Checks.** Three lint rules fail the build in the Portal, Nadia's window and Alveare Server: a page renders a composition (`page-composition`), an absence is only `EmptyState` (`empty-state-owner`), and a title or a label carries no icon (`no-decorative-icon`).
543
543
 
544
544
  One foundation serves four registers. The foundation is every value in this file. A register is one product's way of applying it: the website, the Portal, the Nadia desktop app and the Alveare desktop app ("Registers"). A register adds rules and chooses which values it uses; it never redefines a value or mints one. A value one register needs is authored here under `register.<name>`, and a value two registers share moves to `core`.
545
545
 
@@ -780,7 +780,7 @@ Nadia's window, its list, its Settings and its side panel are Nadia's register (
780
780
 
781
781
  ### Page compositions
782
782
 
783
- A product page is one of five page compositions, each answering one question first and each one component in `@nadicodeai/ui` that draws its order, variants and states from the content the page gives it. A page renders one of them and never assembles its own header, toolbar, table, rows, menus or column; `page-composition` refuses a page that renders a part a composition owns:
783
+ A product page is one of five page compositions, each answering one question first and each one component in `@nadicodeai/ui` that draws its order, variants and states from the content the page gives it. A page renders one of them and never assembles its own header, toolbar, table, rows, menus or column:
784
784
 
785
785
  | Composition | Owner | First answer | Structure |
786
786
  | --- | --- | --- | --- |
@@ -835,7 +835,7 @@ The owner approved the materials: Vercel's surfaces with a little more depth on
835
835
 
836
836
  **Product views on the website** preserve the real interface. Nadia uses captures of the running application, with the unchanged full-size image linked and capture provenance recorded in the repository, following the founder's 12 October correction of invented UI. Portal and Alveare may use inert scenes built from real components with fictional example data recorded in the repository. Never invent interface controls or frame a view in a device mockup. A product window takes the raised height on its ground; a card that overlaps it floats. Live components use dark mode in a black section; captures preserve the application's captured theme.
837
837
 
838
- Absence follows its container, and only a first use has a picture. In every product, the Portal included, a collection's first use shows its section's picture, and every other kind of absence goes without one. A picture sits on a working screen only there: never in a populated collection, a row or any other card; whole-page moments keep theirs ("Entry screens"). A page or region absence is a resting card with its own edge, never a dashed boundary; an absence inside a card is unframed, because the card already owns the edge, and `CollectionPage` puts every absence it draws in its card. `EmptyState` is the only owner of an absence: nothing else draws one (`empty-state-owner`), its `scope` (`page`, `region` or `card`) sets that frame, and it has no presentation option, so every product draws each kind the same way. A composition takes its absences as props (`empty`, `noMatch`, `failed`), as a `DataTable` takes `empty`. Its kind names the cause, and `EmptyState`'s usage page holds what each kind draws: `firstUse` (a collection with no records yet: its section's picture from the approved set in `@nadicodeai/design-system/assets/empty`, which [the art direction](../../skills/design/art-direction/references/surfaces.md#the-empty-state-set) keeps, and the one action that adds the first record, which the page's title row also keeps as its filled primary; a create action carries the leading +), `nothingYet`, `noResults`, `cleared`, `denied` and `failed`; a card-sized failure is `failed` with `scope="card"`, and there is no `ErrorCard`.
838
+ Absence follows its container, and only a first use has a picture. In every product, the Portal included, a collection's first use shows its section's picture, and every other kind of absence goes without one. A picture sits on a working screen only there: never in a populated collection, a row or any other card; whole-page moments keep theirs ("Entry screens"). A page or region absence is a resting card with its own edge, never a dashed boundary; an absence inside a card is unframed, because the card already owns the edge, and `CollectionPage` puts every absence it draws in its card. `EmptyState` is the only owner of an absence: nothing else draws one, its `scope` (`page`, `region` or `card`) sets that frame, and it has no presentation option, so every product draws each kind the same way. A composition takes its absences as props (`empty`, `noMatch`, `failed`), as a `DataTable` takes `empty`. Its kind names the cause, and `EmptyState`'s usage page holds what each kind draws: `firstUse` (a collection with no records yet: its section's picture from the approved set in `@nadicodeai/design-system/assets/empty`, which [the art direction](../../skills/design/art-direction/references/surfaces.md#the-empty-state-set) keeps, and the one action that adds the first record, which the page's title row also keeps as its filled primary; a create action carries the leading +), `nothingYet`, `noResults`, `cleared`, `denied` and `failed`; a card-sized failure is `failed` with `scope="card"`, and there is no `ErrorCard`.
839
839
 
840
840
  A persistent notice sits in the section it concerns, action feedback is a toast, and field validation stays with its field. A notice has no close button: it stays until the state changes, and one note stands for one concept. A section notice is `MessageNotice`; a condition of the page's own object that the page itself resolves, such as a past-due subscription on its billing page, is a `NoticeBanner` carrying that fix; a failure the person must handle is never a toast alone but a very short toast beside a persistent notice. A condition of the system, the setup or the staff (a connection, a stored secret, a failing job, anything a staff member fixes in settings) is a notification in the product's inbox (in the Portal, the bell beside the account at the sidebar's foot), as Vercel keeps certificate, domain and integration alerts in its dashboard's notifications popover ([Vercel's notifications](https://vercel.com/docs/notifications)). It is never a banner on the pages, the Dashboard or the frame they share; the page that fixes it shows the state in its own section. The frame's one strip marks the Portal's Impersonation, because everything done during it is recorded as the Customer's member. A notice, a failed read, a confirmation's error and a toast each show a message its product's catalogue worded, never a sentence written where it shows ([the decision](../../docs/adr/2026-10-08-every-message-a-customer-reads-is-a-code-its-products-catalogue-words.md)). Each wraps a long word, never truncates, closes its title with a full stop before a body that follows on the same line, and shows a body only when it says more than the title; their usage pages hold how each draws it.
841
841
 
@@ -926,7 +926,7 @@ Every example and consuming surface covers its reachable populated, empty, loadi
926
926
  | [Book](https://vercel.com/geist/book) | `Card` + `AspectRatio` and cover artwork; no book-frame export |
927
927
  | [Breadcrumbs](https://vercel.com/geist/breadcrumbs) | The `Breadcrumb` family |
928
928
  | [Browser](https://vercel.com/geist/browser) | `Card` + `AspectRatio` and decorative browser chrome; `DesktopShell` stays real product chrome |
929
- | [Button](https://vercel.com/geist/button) | `Button`, `ButtonLink` and `buttonVariants` |
929
+ | [Button](https://vercel.com/geist/button) | `Button` and `ButtonLink` |
930
930
  | [Calendar](https://vercel.com/geist/calendar) | `Calendar`; `DateRangeControl` for a metric's window |
931
931
  | [Checkbox](https://vercel.com/geist/checkbox) | `Checkbox` with the Field parts |
932
932
  | [Choicebox](https://vercel.com/geist/choicebox) | `ChoiceboxGroup` |
@@ -952,13 +952,13 @@ Every example and consuming surface covers its reachable populated, empty, loadi
952
952
  | [File Tree](https://vercel.com/geist/file-tree) | `SidebarMenu` + `Collapsible` + `FileTypeIcon` can follow sidebar-11 for nested files |
953
953
  | [Gauge](https://vercel.com/geist/gauge) | `Gauge` |
954
954
  | [Grid](https://vercel.com/geist/grid) | Layout grid, `CardRow`, website `nc-page`; no ruled `Grid` API |
955
- | [Input](https://vercel.com/geist/input) | `Input`; `InputGroup` for parts inside the box; `Field` or `SettingsField` for the card around it |
955
+ | [Input](https://vercel.com/geist/input) | `Input`, with `prefix` and `suffix` for parts inside the box (`InputGroup` stays inside `@nadicodeai/ui`); `Field` or `SettingsField` for the card around it |
956
956
  | [JSON View](https://vercel.com/geist/json-view) | Composition for static JSON: code display with exact serialized text |
957
957
  | [Keyboard Input](https://vercel.com/geist/keyboard-input) | `Kbd` |
958
958
  | [Label](https://vercel.com/geist/label) | `Label`, `FieldLabel` and `FieldLegend` |
959
959
  | [Load More Button](https://vercel.com/geist/load-more-button) | `LoadMoreButton` |
960
960
  | [Loading Dots](https://vercel.com/geist/loading-dots) | `Button`'s `loading` and `WaitingLine`; no LoadingDots |
961
- | [Menu](https://vercel.com/geist/menu) | `ActionMenu`, `ActionContextMenu`, `PageActions`' `menu` and `AccountMenu`; the `DropdownMenu` family directly only for a choice menu such as a sort |
961
+ | [Menu](https://vercel.com/geist/menu) | `ActionMenu`, `ActionContextMenu`, `PageActions`' `menu`, `AccountMenu` and the website's `SiteMenu`; the `DropdownMenu` family stays inside `@nadicodeai/ui`, which draws a choice menu such as a sort with it |
962
962
  | [MiddleTruncate](https://vercel.com/geist/middle-truncate) | `MiddleTruncate` |
963
963
  | [Modal](https://vercel.com/geist/modal) | The `Dialog` family, `FormDialog` for a short form and `ConfirmDialog` for a consequence |
964
964
  | [Multi Select](https://vercel.com/geist/multi-select) | `MultiSelect`; `Combobox multiple` when typing to find matters more; `CollectionFilters` for query facets |
@@ -1067,7 +1067,7 @@ A card or settings section is composed around a real task, collection or form; i
1067
1067
 
1068
1068
  ### Menus and commands
1069
1069
 
1070
- A record's primary destination is its visible name or a labelled link. Navigation is never hidden in its action menu. Keep the main task visible: Edit opens the record inside its row, as in Vercel Domains; Connect belongs beside an available connection. Additional mutations use an end-aligned `ActionMenu`, the same commands on a right-click through `ActionContextMenu`; a menu is not required when the row has no secondary mutations. Commands reach a person only through `PageActions` (a page), `ActionMenu` and `ActionContextMenu` (a record) and `AccountMenu` (the account), each taking its commands as data (`menu-owner`). A menu names the record it acts on, keeps a command that cannot run in place with its reason, and puts the destructive command last after a divider; `ActionMenu`'s usage page holds how it draws them, and `AccountMenu`'s the account menu's one exception. A destructive command never becomes the loudest repeated action: it is never a button on every row.
1070
+ A record's primary destination is its visible name or a labelled link. Navigation is never hidden in its action menu. Keep the main task visible: Edit opens the record inside its row, as in Vercel Domains; Connect belongs beside an available connection. Additional mutations use an end-aligned `ActionMenu`, the same commands on a right-click through `ActionContextMenu`; a menu is not required when the row has no secondary mutations. Commands reach a person only through `PageActions` (a page), `ActionMenu` and `ActionContextMenu` (a record) and `AccountMenu` (the account), each taking its commands as data. A menu names the record it acts on, keeps a command that cannot run in place with its reason, and puts the destructive command last after a divider; `ActionMenu`'s usage page holds how it draws them, and `AccountMenu`'s the account menu's one exception. A destructive command never becomes the loudest repeated action: it is never a button on every row.
1071
1071
 
1072
1072
  Choose the surface by where the thing being changed is shown and how big the job is, never by the page that happens to need it: the same job takes the same surface in every product. Adding a skill is the catalogue drawer in the Portal and in Nadia, and editing a task or an MCP server opens its row in both. Each surface has one owner:
1073
1073
 
@@ -1089,7 +1089,7 @@ Collection search and filters stay immediately above their rows. The title row h
1089
1089
 
1090
1090
  ### Tables and missing values
1091
1091
 
1092
- `Table`, `DataTable`, `TableCard`, `RecordList`, `Item` and `Tag` hold their geometry, columns and rows on their usage pages; `page-composition` refuses a `DataTable` without column headings. No heading sorts the rows, so no heading is a button: the page holds the order, through a `CollectionSort` above the list, and `aria-sort` marks the column the rows follow.
1092
+ `Table`, `DataTable`, `TableCard`, `RecordList`, `Item` and `Tag` hold their geometry, columns and rows on their usage pages; a `DataTable` has column headings. No heading sorts the rows, so no heading is a button: the page holds the order, through a `CollectionSort` above the list, and `aria-sort` marks the column the rows follow.
1093
1093
 
1094
1094
  A collection fits its container without sideways scrolling, and on a phone a row keeps its name above supporting facts that wrap in full, never a stack of labelled values, where a status keeps its own mark; `DataTable`'s and `RecordList`'s usage pages hold how their columns give way. Text anywhere in the product breaks between words, and inside a word only when that word alone cannot fit a line; a machine string such as a hash or a key breaks at any character in wrapped text, or is cut in the middle with `MiddleTruncate` on one line.
1095
1095
 
@@ -1185,7 +1185,7 @@ Three icons are the house's own, drawn on Lucide's grid in the same render: `arc
1185
1185
 
1186
1186
  The blue category uses the cobalto 200 ground and cobalto 1000 ink through `category-blue` and `category-blue-foreground`, with the same ramp steps in each theme. It never uses the status roles `info` or `info-soft`.
1187
1187
 
1188
- An icon belongs to a control or a menu row, never to a title. A card's, a section's or a label's title and a KPI's label carry no icon (`no-decorative-icon`): an icon there repeats the words beside it and competes with the marks that matter. A menu row takes one icon, leading its label; a button takes one, leading or trailing it (`prefix` or `suffix`). A mark goes where it says which thing you are looking at; a picture marks a moment. Where each goes:
1188
+ An icon belongs to a control or a menu row, never to a title. A card's, a section's or a label's title and a KPI's label carry no icon: an icon there repeats the words beside it and competes with the marks that matter. A menu row takes one icon, leading its label; a button takes one, leading or trailing it (`prefix` or `suffix`). A mark goes where it says which thing you are looking at; a picture marks a moment. Where each goes:
1189
1189
 
1190
1190
  | Place | Identity mark | Icon | Picture |
1191
1191
  | --- | --- | --- | --- |
package/README.md CHANGED
@@ -106,7 +106,7 @@ Consumer overrides belong in an explicit later layer:
106
106
 
107
107
  ## How the build works
108
108
 
109
- `npm run build -w @nadicodeai/design-system` reads two sources: the
109
+ `pnpm --filter @nadicodeai/design-system build` reads two sources: the
110
110
  `@google/design.md` CLI exports `DESIGN.md`'s YAML front matter, and
111
111
  `scripts/sd/token-extension.ts` reads the token extension, `src/tokens/extension.tokens.json`.
112
112
  `scripts/sd/compose-token-graph.ts` joins them into the single `nadicode` token
@@ -125,7 +125,7 @@ host with base styles of its own). `src/css/index.css` declares the cascade's
125
125
  layer order.
126
126
  Generated files and `dist/` are outputs, which git does not track: edit
127
127
  `DESIGN.md` or `src/tokens/extension.tokens.json` and rebuild, never the output.
128
- Every install builds them through the root `prepare`, and `tests/guards/` holds
128
+ A worktree's preparation and every CI job's setup build them, and `tests/guards/` holds
129
129
  the structural checks on every output.
130
130
  The favicons are the exception: `generate:favicons` writes them and git tracks
131
131
  them. A consumer reaches any output through the package's `exports`, never a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nadicodeai/design-system",
3
- "version": "12.1.165",
3
+ "version": "12.1.166",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -233,44 +233,36 @@
233
233
  "engines": {
234
234
  "node": ">=22"
235
235
  },
236
- "scripts": {
237
- "build": "tsx scripts/build.ts",
238
- "generate:icons": "tsx scripts/generate-icons.ts",
239
- "generate:favicons": "tsx scripts/generate-favicons.ts",
240
- "lint:design": "design.md lint DESIGN.md",
241
- "lint": "npm run lint:oxlint && node ../../tools/workspace/heavy-slots.mjs -- eslint --max-warnings=0",
242
- "lint:oxlint": "node ../../tools/workspace/heavy-slots.mjs -- oxlint",
243
- "typecheck": "node ../../tools/workspace/heavy-slots.mjs -- node ../../node_modules/typescript-native/bin/tsc --checkers 2 -p tsconfig.json",
244
- "check": "npm run lint:design && npm run lint && npm run typecheck",
245
- "check:static": "npm run check",
246
- "test": "node ../../tools/workspace/heavy-slots.mjs --skip-if-filtered -- vitest run",
247
- "test:visual": "node ../../tools/workspace/heavy-slots.mjs -- playwright test --config playwright.visual.config.ts",
248
- "capture:geist": "node scripts/capture-geist.mjs",
249
- "prepublishOnly": "npm run build"
250
- },
251
236
  "devDependencies": {
252
237
  "@google/design.md": "0.4.0",
253
- "@nadicodeai/eslint-config": "*",
254
- "@nadicodeai/eslint-plugin": "*",
255
- "@nadicodeai/oxlint-config": "*",
256
- "@nadicodeai/static-design-server": "*",
257
- "@nadicodeai/test-support": "*",
238
+ "@nadicodeai/static-design-server": "0.0.0",
239
+ "@nadicodeai/test-support": "0.0.0",
258
240
  "@playwright/test": "^1.64.1",
259
241
  "@types/node": "^24",
260
242
  "ajv": "8.20.0",
261
243
  "ajv-formats": "3.0.1",
262
244
  "esbuild": "^0.28.2",
263
- "eslint": "^9.39.4",
264
245
  "lucide-static": "1.58.0",
265
- "oxlint": "1.88.0",
266
- "parse5": "^8.0.1",
267
246
  "postcss": "^8.5.30",
268
- "postcss-value-parser": "^4.2.0",
247
+ "publint": "^0.3.25",
269
248
  "sharp": "^0.35.5",
270
249
  "style-dictionary": "^5.6.1",
271
250
  "tsx": "^4.23.15",
272
251
  "typescript": "^6",
273
252
  "typescript-native": "npm:typescript@7.0.2",
274
253
  "vitest": "^5.0.5"
254
+ },
255
+ "scripts": {
256
+ "build": "tsx scripts/build.ts",
257
+ "capture:geist": "node scripts/capture-geist.mjs",
258
+ "check:package": "publint --strict",
259
+ "generate:favicons": "tsx scripts/generate-favicons.ts",
260
+ "generate:icons": "tsx scripts/generate-icons.ts",
261
+ "lint": "oxlint -c ../../oxlint.config.ts",
262
+ "lint:design": "design.md lint DESIGN.md",
263
+ "lint:types": "oxlint -c ../../oxlint.types.config.ts",
264
+ "test": "vitest run",
265
+ "test:visual": "playwright test --config playwright.visual.config.ts",
266
+ "typecheck": "node node_modules/typescript-native/bin/tsc --checkers 2 -p tsconfig.json"
275
267
  }
276
- }
268
+ }