@grampro/kit 2.4.4 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/CHANGELOG.md +169 -0
  2. package/index.cjs +39 -4
  3. package/package.json +1 -1
  4. package/source/components/accordion/passport.json +11 -7
  5. package/source/components/accordion/react/Accordion.tsx +5 -0
  6. package/source/components/alert/passport.json +3 -3
  7. package/source/components/avatar/passport.json +8 -6
  8. package/source/components/avatar/react/Avatar.tsx +4 -0
  9. package/source/components/badge/passport.json +8 -6
  10. package/source/components/badge/react/Badge.tsx +1 -0
  11. package/source/components/badge/react/Tag.tsx +1 -0
  12. package/source/components/breadcrumb/passport.json +8 -6
  13. package/source/components/breadcrumb/react/Breadcrumb.tsx +1 -0
  14. package/source/components/button/passport.json +8 -6
  15. package/source/components/button/react/Button.tsx +1 -0
  16. package/source/components/card/passport.json +17 -9
  17. package/source/components/card/react/Card.tsx +5 -0
  18. package/source/components/card/react/Stat.tsx +5 -0
  19. package/source/components/checkbox/passport.json +32 -14
  20. package/source/components/checkbox/react/Checkbox.tsx +17 -0
  21. package/source/components/checkbox/react/CheckboxGroup.tsx +6 -0
  22. package/source/components/combobox/passport.json +36 -16
  23. package/source/components/combobox/react/Select.tsx +6 -1
  24. package/source/components/combobox/react/props.ts +23 -0
  25. package/source/components/data-grid/core/types.ts +53 -0
  26. package/source/components/data-grid/passport.json +82 -30
  27. package/source/components/data-grid/react/AskGrid.tsx +10 -0
  28. package/source/components/data-grid/react/DataGrid.tsx +16 -0
  29. package/source/components/date-picker/passport.json +30 -14
  30. package/source/components/date-picker/react/DatePicker.tsx +5 -0
  31. package/source/components/date-picker/react/props.ts +13 -1
  32. package/source/components/dialog/core/types.ts +14 -0
  33. package/source/components/dialog/passport.json +26 -12
  34. package/source/components/dialog/react/Dialog.tsx +4 -0
  35. package/source/components/file-uploader/passport.json +44 -18
  36. package/source/components/file-uploader/react/FileUploader.tsx +39 -0
  37. package/source/components/input/passport.json +14 -8
  38. package/source/components/input/react/Input.tsx +3 -0
  39. package/source/components/menu/passport.json +29 -13
  40. package/source/components/menu/react/Menu.tsx +7 -0
  41. package/source/components/menu/react/items.tsx +4 -0
  42. package/source/components/modal/passport.json +14 -8
  43. package/source/components/modal/react/Modal.tsx +6 -0
  44. package/source/components/number-input/passport.json +23 -11
  45. package/source/components/number-input/react/NumberInput.tsx +13 -0
  46. package/source/components/popover/passport.json +17 -9
  47. package/source/components/popover/react/Popover.tsx +7 -0
  48. package/source/components/progress/passport.json +3 -3
  49. package/source/components/radio-group/passport.json +32 -14
  50. package/source/components/radio-group/react/Radio.tsx +17 -0
  51. package/source/components/radio-group/react/RadioGroup.tsx +13 -0
  52. package/source/components/skeleton/passport.json +8 -6
  53. package/source/components/skeleton/react/Skeleton.tsx +5 -0
  54. package/source/components/spinner/passport.json +3 -3
  55. package/source/components/switch/passport.json +23 -11
  56. package/source/components/switch/react/Switch.tsx +14 -0
  57. package/source/components/tabs/passport.json +11 -7
  58. package/source/components/tabs/react/Tabs.tsx +2 -0
  59. package/source/components/textarea/passport.json +14 -8
  60. package/source/components/textarea/react/Textarea.tsx +3 -0
  61. package/source/components/toaster/passport.json +8 -6
  62. package/source/components/toaster/react/Toaster.tsx +4 -0
  63. package/source/components/tooltip/passport.json +3 -3
  64. package/tools/mcp/README.md +126 -34
  65. package/tools/mcp/figma.cjs +84 -27
  66. package/tools/mcp/install.cjs +270 -0
  67. package/tools/mcp/secret-cli.cjs +174 -0
  68. package/tools/mcp/secret.cjs +289 -0
  69. package/tools/mcp/server.cjs +53 -14
  70. package/tools/passport/cli.cjs +8 -1
  71. package/tools/passport/extract.cjs +7 -3
  72. package/tools/passport/merge.cjs +24 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,174 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.5.0
4
+
5
+ ### Added
6
+
7
+ **Every prop this library declares now carries a description.**
8
+
9
+ 117 did not, and the passports are what an agent reads instead of the
10
+ source, so an undescribed prop is one it has to guess at. The worst of them
11
+ were not obscure: `DataGrid`'s `columns` and `data` had no description at
12
+ all, and `label`, `error`, `description` and `onChange` were bare on
13
+ Checkbox, Switch and RadioGroup.
14
+
15
+ Where reading the code turned up something a type cannot say, that is in
16
+ the description now:
17
+
18
+ - `onCellEdit` fires only when the value actually changed, and returning a
19
+ promise makes the edit optimistic - the cell reverts and shows the
20
+ rejection's message if it fails, so reject with the reason you want read.
21
+ - `CheckboxGroup`'s values accumulate in the order they were checked, not
22
+ the order the options are declared, so sort before comparing selections.
23
+ - `Checkbox.onCheckedChange` never fires with `"indeterminate"`.
24
+ - `FileUploader` treats name, size and last-modified time as a file's
25
+ identity when rejecting duplicates.
26
+ - `NumberInput`'s `min` and `max` apply on blur rather than per keystroke,
27
+ unless `clampBehavior` says otherwise.
28
+ - `DatePicker.onChange` always hands back a `Date` at local midnight, even
29
+ though `value` accepts a string or a number.
30
+ **The Figma token belongs to your operating system now, not to a file in
31
+ your repository.**
32
+
33
+ ```bash
34
+ npx @grampro/kit secret set figma
35
+ ```
36
+
37
+ It prompts with the echo off and stores the token where the platform keeps
38
+ credentials: the Data Protection API on Windows, the keychain on macOS, the
39
+ Secret Service on Linux. Anywhere else it falls back to a 0600 file in your
40
+ config directory and says so rather than implying otherwise. The value is
41
+ never passed as an argument, because arguments land in shell history and in
42
+ every process listing on the machine; pipe it instead if you need to script
43
+ it.
44
+
45
+ `secret list` shows what is stored, masked, and names the backend. `secret
46
+ remove figma` deletes it.
47
+
48
+ The old `.env` route still works, so upgrading breaks nothing, but reading
49
+ from one now prints a note telling you to move it. A credential sitting in a
50
+ working tree is one `git add -A` away from being published, which is the
51
+ whole reason for the change.
52
+
53
+ **`mcp install` writes the configuration for you.**
54
+
55
+ ```bash
56
+ npx @grampro/kit mcp install # every agent it knows
57
+ npx @grampro/kit mcp install --for codex
58
+ ```
59
+
60
+ Claude Code, Codex and Cursor each take the same stdio server and differ
61
+ only in where the configuration lives and what format it is in. This writes
62
+ `.mcp.json`, `.codex/config.toml` and `.cursor/mcp.json` respectively,
63
+ merging into whatever is already there and leaving other servers alone.
64
+
65
+ It is worth preferring over editing those files by hand, which is how this
66
+ goes wrong: PowerShell's `Out-File -Encoding utf8` adds a byte-order mark,
67
+ and every JSON parser then rejects the file with an error naming the first
68
+ character and explaining nothing. This writes UTF-8 without a BOM and with
69
+ LF endings, and strips a BOM from a file that already has one.
70
+
71
+ It writes to the project root — the nearest folder above you with a `.git`
72
+ or a `package.json` — and prints which one it chose. An agent reads these
73
+ files from the folder it has open, so running the command from a
74
+ subdirectory would have written somewhere nothing ever looks while still
75
+ reporting success.
76
+
77
+ It also now says what has to happen next, because writing the file is not
78
+ the same as loading the server. A `.mcp.json` server is project-scoped and
79
+ Claude Code asks before trusting one, so it sits at `Pending approval` until
80
+ you agree; and the configuration is read when a session starts, so an editor
81
+ window that was already open sees nothing until it reloads. Installing at
82
+ user scope — `claude mcp add -s user grampro -- npx -y @grampro/kit mcp` —
83
+ skips both, at the cost of not being shareable through a commit.
84
+
85
+ ### Changed
86
+
87
+ **Reading a Figma design no longer downloads the whole file.**
88
+
89
+ Figma charges for file reads by the size of the response, and the frame
90
+ listing was fetching the entire document — 710 KB on the file this was
91
+ tested against — to answer a question about one screen. Two of those in a
92
+ row is what earns a 429, and the lockout is long: a `Retry-After` of 395274
93
+ was observed on a single token.
94
+
95
+ The listing now uses `depth=1`, which returns the document's outline rather
96
+ than its contents, and the chosen frame is fetched by id. A URL that already
97
+ carries a `node-id` — what Figma copies when you right-click a frame — goes
98
+ straight there in one call. Responses are still held for five minutes, so
99
+ the usual list-then-read flow costs one outline and one frame.
100
+
101
+ If a token is already rate limited, the Figma plugin export route still
102
+ works; it reads the scene graph from inside Figma and never calls the API.
103
+
104
+ **The MCP server starts in about the time an empty Node process takes.**
105
+
106
+ An agent spawns the server once per session, and it was loading the CLI's
107
+ own dependencies first — `fs-extra` and `yargs`, roughly 70ms, and of no use
108
+ to a JSON-RPC loop. The `mcp` command is now routed before they load.
109
+
110
+ The passports are also indexed on the first question rather than re-read
111
+ from disk for each one.
112
+
113
+ ### Fixed
114
+
115
+ **`Select`'s `closeOnSelect` was documented backwards, in both directions.**
116
+ Its description read "Keep the list open after choosing. Default false."
117
+ while the code defaults it to `true` and closing is what the prop does. An
118
+ agent reading that passport would have had both the behaviour and the
119
+ default inverted - the failure mode that makes a wrong description worse
120
+ than a missing one. MultiSelect's was already right.
121
+
122
+ **Description coverage was measured against a denominator nobody could
123
+ move.** It counted every prop, including the HTML and ARIA attributes that
124
+ arrive through `extends ButtonHTMLAttributes` - 390 of the 507 undescribed
125
+ props were those. So Button sat at 31% with its entire own API documented
126
+ bar one prop, and eleven components stayed under the 40% threshold with
127
+ nothing left to write. A gate that cannot be satisfied by doing the work it
128
+ asks for gets ignored.
129
+
130
+ It now measures the props the library authors, excluding inherited
131
+ attributes and the handful whose meaning is library-wide (`className`,
132
+ `style`, `ref` and friends) rather than restating them 27 times. On that
133
+ measure the library is at 87% and the weakest component at 75%, so the
134
+ threshold moves from 40 to 75 - a floor rather than an aspiration.
135
+ **The rate-limit message no longer promises that waiting a minute will
136
+ help.** It said to try again shortly, which is wrong for a cost-based limit
137
+ whose window runs to tens of minutes or longer. It now passes Figma's own
138
+ `Retry-After` through as given, without asserting a unit — the value has
139
+ been seen in a range where seconds and milliseconds differ by three orders
140
+ of magnitude — and points at the plugin route, which cannot be rate limited.
141
+
142
+ ## 2.4.4
143
+
144
+ ### Added
145
+
146
+ **An MCP server, so an agent can ask about a component instead of recalling
147
+ it.**
148
+
149
+ ```bash
150
+ npx @grampro/kit mcp
151
+ ```
152
+
153
+ Seven tools: `list_components`, `get_component`, `get_example`,
154
+ `get_guidance`, `figma_frame`, `install_component` and `validate_usage`. The
155
+ catalogue is 280 KB of passports and a page needs two or three components,
156
+ which is the argument for answering on demand rather than pasting a document
157
+ into the prompt.
158
+
159
+ `validate_usage` is the one that earns its place. A page written from
160
+ passports alone compiled on the first attempt apart from a single prop the
161
+ model had remembered from elsewhere — every type, union value and handler
162
+ signature the passports carried came out right. So the remaining failure
163
+ mode is invention, and that is what this checks.
164
+
165
+ No dependencies: MCP is newline-delimited JSON-RPC over stdio, and a server
166
+ shipped inside a library every consumer installs should not drag an SDK
167
+ along with it.
168
+
169
+ Marked beta — the tools work and are tested, but their names and shapes may
170
+ change.
171
+
3
172
  ## 2.4.3
4
173
 
5
174
  ### Fixed
package/index.cjs CHANGED
@@ -1,5 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ /*
4
+ * The MCP server starts before the CLI does, and deliberately.
5
+ *
6
+ * An agent spawns the server once per session, and this file's own
7
+ * dependencies — fs-extra and yargs — cost about 70ms to load and are of no
8
+ * use to a JSON-RPC loop. Routing here first takes the server's start-up down
9
+ * to roughly what an empty Node process costs. A top-level `return` is valid
10
+ * in CommonJS, where a module body is already wrapped in a function.
11
+ *
12
+ * Only bare `mcp` is the server. Anything with a word after it — `install`,
13
+ * or a misspelling of it — is the CLI's business and falls through, so that a
14
+ * typo gets an answer instead of silently becoming a server waiting on stdin.
15
+ */
16
+ if (process.argv[2] === "mcp" && !process.argv.slice(3).some((arg) => !arg.startsWith("-"))) {
17
+ // From here stdout carries JSON-RPC; nothing else may write to it.
18
+ require("./tools/mcp/server.cjs").main();
19
+ return;
20
+ }
21
+
3
22
  const fs = require("fs-extra");
4
23
  const path = require("path");
5
24
  const crypto = require("crypto");
@@ -1171,7 +1190,7 @@ const validateComponents = (components, beta = false) => {
1171
1190
  * projects a week install this; breaking their scripts to tidy an argument
1172
1191
  * parser is not a trade worth making.
1173
1192
  */
1174
- const COMMANDS = new Set(["add", "list", "skill", "passport", "mcp"]);
1193
+ const COMMANDS = new Set(["add", "list", "skill", "passport", "mcp", "secret"]);
1175
1194
 
1176
1195
  /**
1177
1196
  * Flags that take a separate value, so the token after one is that value and
@@ -1244,7 +1263,7 @@ const main = async () => {
1244
1263
  })
1245
1264
  .option("for", {
1246
1265
  describe:
1247
- "With skill: which agents to write adapters for (claude, codex, antigravity, or none)",
1266
+ "With skill or mcp install: which agents (claude, codex, antigravity, cursor, all)",
1248
1267
  type: "string",
1249
1268
  })
1250
1269
  .option("passport", {
@@ -1275,6 +1294,9 @@ const main = async () => {
1275
1294
  .example("$0 add Select --legacy", "Install from the 1.x set")
1276
1295
  .example("$0 add Button --dest app/ui", "Choose where they go")
1277
1296
  .example("$0 mcp", "Serve the catalogue to a coding agent over MCP")
1297
+ .example("$0 mcp install", "Register the MCP server with every agent found")
1298
+ .example("$0 mcp install --for codex", "Only write the Codex config")
1299
+ .example("$0 secret set figma", "Store a Figma token in the OS credential store")
1278
1300
  .example("$0 -i", "Interactive selection")
1279
1301
  .help().argv;
1280
1302
 
@@ -1293,8 +1315,21 @@ const main = async () => {
1293
1315
  }
1294
1316
 
1295
1317
  if (parsed && parsed.command === "mcp") {
1296
- // Speaks JSON-RPC on stdout; nothing else may write there.
1297
- require("./tools/mcp/server.cjs").main();
1318
+ // Bare `mcp` never reaches this far: it is handled at the top of the file,
1319
+ // before the CLI's dependencies load. What arrives here is `mcp install`,
1320
+ // and anything else is a typo worth naming.
1321
+ if (parsed.operands[0] === "install") {
1322
+ require("./tools/mcp/install.cjs").main(parsed.operands.slice(1), { for: argv.for });
1323
+ return;
1324
+ }
1325
+ console.log(
1326
+ `Did you mean \`${CLI_NAME} mcp\` (run the server) or \`${CLI_NAME} mcp install\` (configure an agent)?`,
1327
+ );
1328
+ return;
1329
+ }
1330
+
1331
+ if (parsed && parsed.command === "secret") {
1332
+ await require("./tools/mcp/secret-cli.cjs").main(parsed.operands);
1298
1333
  return;
1299
1334
  }
1300
1335
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grampro/kit",
3
- "version": "2.4.4",
3
+ "version": "2.5.0",
4
4
  "description": "GramproKit \u2014 an agent-native UI runtime for React. Copy-in components you own, each publishing a contract that software can read.",
5
5
  "type": "module",
6
6
  "files": [
@@ -10,7 +10,7 @@
10
10
  ]
11
11
  },
12
12
  "source": {
13
- "libraryVersion": "2.4.4",
13
+ "libraryVersion": "2.5.0",
14
14
  "files": [
15
15
  "core/index.ts",
16
16
  "core/open.ts",
@@ -21,7 +21,7 @@
21
21
  "react/context.ts",
22
22
  "react/props.ts"
23
23
  ],
24
- "sourceHash": "sha256:719b5c18a3e0f3f7479cd1c4f59d729b335053fc47708cc5ab5763b6d69a206a"
24
+ "sourceHash": "sha256:c04b9c09e8661f463e2a808ecd45a7c080c236bc7b398efc1b3505a45e245f00"
25
25
  },
26
26
  "props": [
27
27
  {
@@ -60,7 +60,9 @@
60
60
  "type": "other",
61
61
  "typeText": "string[] | undefined",
62
62
  "required": false,
63
- "origin": "derived"
63
+ "origin": "derived",
64
+ "description": "Panels open to begin with, when you are not controlling `value`.",
65
+ "descriptionOrigin": "jsdoc"
64
66
  },
65
67
  {
66
68
  "name": "description",
@@ -135,7 +137,9 @@
135
137
  "type": "function",
136
138
  "signature": "((value: string[]) => void) | undefined",
137
139
  "required": false,
138
- "origin": "derived"
140
+ "origin": "derived",
141
+ "description": "The open panels after a change, always an array even with `multiple`\r\noff - where it holds one value, or none once a panel is closed.",
142
+ "descriptionOrigin": "jsdoc"
139
143
  },
140
144
  {
141
145
  "name": "ref",
@@ -249,9 +253,9 @@
249
253
  "safeMutations": {},
250
254
  "examples": [],
251
255
  "coverage": {
252
- "propsTotal": 19,
253
- "propsDescribed": 10,
254
- "describedPct": 53,
256
+ "propsTotal": 15,
257
+ "propsDescribed": 12,
258
+ "describedPct": 80,
255
259
  "authoredSections": [
256
260
  "accessibility"
257
261
  ]
@@ -22,7 +22,12 @@ import { cx, type AccordionSlot } from "./props";
22
22
  export interface AccordionProps {
23
23
  /** Open panels, by value (controlled). */
24
24
  value?: string[];
25
+ /** Panels open to begin with, when you are not controlling `value`. */
25
26
  defaultValue?: string[];
27
+ /**
28
+ * The open panels after a change, always an array even with `multiple`
29
+ * off - where it holds one value, or none once a panel is closed.
30
+ */
26
31
  onValueChange?(value: string[]): void;
27
32
  /** Render these panels, or pass `<AccordionItem>` children. */
28
33
  items?: readonly AccordionItemData[];
@@ -8,7 +8,7 @@
8
8
  ]
9
9
  },
10
10
  "source": {
11
- "libraryVersion": "2.4.4",
11
+ "libraryVersion": "2.5.0",
12
12
  "files": [
13
13
  "core/index.ts",
14
14
  "core/live.ts",
@@ -240,9 +240,9 @@
240
240
  "safeMutations": {},
241
241
  "examples": [],
242
242
  "coverage": {
243
- "propsTotal": 19,
243
+ "propsTotal": 8,
244
244
  "propsDescribed": 6,
245
- "describedPct": 32,
245
+ "describedPct": 75,
246
246
  "authoredSections": [
247
247
  "accessibility"
248
248
  ]
@@ -9,7 +9,7 @@
9
9
  ]
10
10
  },
11
11
  "source": {
12
- "libraryVersion": "2.4.4",
12
+ "libraryVersion": "2.5.0",
13
13
  "files": [
14
14
  "core/identity.ts",
15
15
  "core/index.ts",
@@ -20,7 +20,7 @@
20
20
  "react/locale.ts",
21
21
  "react/props.ts"
22
22
  ],
23
- "sourceHash": "sha256:2e50d4901b3c6a436a1c8ee35fb43eb11488e305b0466874f007ee325aa02896"
23
+ "sourceHash": "sha256:541aa6ca5c99e64fba414881c07cc3c29244e03c09b665a4934690d633cc20e1"
24
24
  },
25
25
  "props": [
26
26
  {
@@ -186,7 +186,9 @@
186
186
  "type": "string",
187
187
  "typeText": "string | undefined",
188
188
  "required": false,
189
- "origin": "derived"
189
+ "origin": "derived",
190
+ "description": "The image. If it fails to load, the avatar falls back to the initials\nrather than showing a broken image, so `name` is worth setting too.",
191
+ "descriptionOrigin": "jsdoc"
190
192
  },
191
193
  {
192
194
  "name": "status",
@@ -284,9 +286,9 @@
284
286
  "safeMutations": {},
285
287
  "examples": [],
286
288
  "coverage": {
287
- "propsTotal": 23,
288
- "propsDescribed": 9,
289
- "describedPct": 39,
289
+ "propsTotal": 10,
290
+ "propsDescribed": 10,
291
+ "describedPct": 100,
290
292
  "authoredSections": [
291
293
  "accessibility"
292
294
  ]
@@ -22,6 +22,10 @@ export interface AvatarProps extends Omit<HTMLAttributes<HTMLSpanElement>, "chil
22
22
  * for the colour, so the same person is always the same colour.
23
23
  */
24
24
  name?: string;
25
+ /**
26
+ * The image. If it fails to load, the avatar falls back to the initials
27
+ * rather than showing a broken image, so `name` is worth setting too.
28
+ */
25
29
  src?: string;
26
30
  /** Overrides the text derived from `name`. */
27
31
  initials?: string;
@@ -9,7 +9,7 @@
9
9
  ]
10
10
  },
11
11
  "source": {
12
- "libraryVersion": "2.4.4",
12
+ "libraryVersion": "2.5.0",
13
13
  "files": [
14
14
  "core/count.ts",
15
15
  "core/index.ts",
@@ -20,7 +20,7 @@
20
20
  "react/locale.ts",
21
21
  "react/props.ts"
22
22
  ],
23
- "sourceHash": "sha256:f6db45647bfab522ced894c388fb16f6a7fac5ebd20a7114d351b812a1f73d34"
23
+ "sourceHash": "sha256:16f626a94da3bac9b1a105718010edd7e1ce5a6118823ed8a53c97d658d89a3e"
24
24
  },
25
25
  "props": [
26
26
  {
@@ -128,7 +128,9 @@
128
128
  "type": "other",
129
129
  "typeText": "ReactNode",
130
130
  "required": false,
131
- "origin": "derived"
131
+ "origin": "derived",
132
+ "description": "An icon before the label, in place of `dot`.",
133
+ "descriptionOrigin": "jsdoc"
132
134
  },
133
135
  {
134
136
  "name": "id",
@@ -322,9 +324,9 @@
322
324
  "safeMutations": {},
323
325
  "examples": [],
324
326
  "coverage": {
325
- "propsTotal": 26,
326
- "propsDescribed": 10,
327
- "describedPct": 38,
327
+ "propsTotal": 13,
328
+ "propsDescribed": 11,
329
+ "describedPct": 85,
328
330
  "authoredSections": [
329
331
  "accessibility"
330
332
  ]
@@ -23,6 +23,7 @@ export interface BadgeProps extends Omit<HTMLAttributes<HTMLSpanElement>, "child
23
23
  formatValue?(value: number): string;
24
24
  /** A small filled circle before the label, for a status. */
25
25
  dot?: boolean;
26
+ /** An icon before the label, in place of `dot`. */
26
27
  icon?: ReactNode;
27
28
  classNames?: Partial<Record<BadgeSlot, string>>;
28
29
  ref?: Ref<HTMLSpanElement>;
@@ -11,6 +11,7 @@ export interface TagProps extends Omit<HTMLAttributes<HTMLSpanElement>, "onRemov
11
11
  variant?: BadgeVariant;
12
12
  appearance?: BadgeAppearance;
13
13
  size?: BadgeSize;
14
+ /** An icon before the label. */
14
15
  icon?: ReactNode;
15
16
  /** Show a remove button. Without it a Tag is a Badge with a bit more padding. */
16
17
  onRemove?(): void;
@@ -8,7 +8,7 @@
8
8
  ]
9
9
  },
10
10
  "source": {
11
- "libraryVersion": "2.4.4",
11
+ "libraryVersion": "2.5.0",
12
12
  "files": [
13
13
  "core/index.ts",
14
14
  "core/trail.ts",
@@ -18,7 +18,7 @@
18
18
  "react/locale.ts",
19
19
  "react/props.ts"
20
20
  ],
21
- "sourceHash": "sha256:ea18a9023e99fd751d8295a40f257e4471948a5cc9878d808501471718da32aa"
21
+ "sourceHash": "sha256:3b7fef8f48d1a1290c25a263d2e92ef5dcb706b1a06899ffe5a4df1cdab663e1"
22
22
  },
23
23
  "props": [
24
24
  {
@@ -151,7 +151,9 @@
151
151
  "typeText": "'sm' | 'md' | 'lg' | undefined",
152
152
  "required": false,
153
153
  "default": "md",
154
- "origin": "derived"
154
+ "origin": "derived",
155
+ "description": "Default `md`.",
156
+ "descriptionOrigin": "jsdoc"
155
157
  },
156
158
  {
157
159
  "name": "structuredData",
@@ -233,9 +235,9 @@
233
235
  "safeMutations": {},
234
236
  "examples": [],
235
237
  "coverage": {
236
- "propsTotal": 20,
237
- "propsDescribed": 7,
238
- "describedPct": 35,
238
+ "propsTotal": 8,
239
+ "propsDescribed": 8,
240
+ "describedPct": 100,
239
241
  "authoredSections": [
240
242
  "accessibility"
241
243
  ]
@@ -32,6 +32,7 @@ export interface BreadcrumbProps extends Omit<HTMLAttributes<HTMLElement>, "chil
32
32
  * `{ baseUrl }` so relative links become absolute, as search engines require.
33
33
  */
34
34
  structuredData?: boolean | { baseUrl: string };
35
+ /** Default `md`. */
35
36
  size?: "sm" | "md" | "lg";
36
37
  classNames?: Partial<Record<BreadcrumbSlot, string>>;
37
38
  localeText?: Partial<BreadcrumbLocaleText>;
@@ -8,7 +8,7 @@
8
8
  ]
9
9
  },
10
10
  "source": {
11
- "libraryVersion": "2.4.4",
11
+ "libraryVersion": "2.5.0",
12
12
  "files": [
13
13
  "core/index.ts",
14
14
  "core/state.ts",
@@ -18,7 +18,7 @@
18
18
  "react/locale.ts",
19
19
  "react/props.ts"
20
20
  ],
21
- "sourceHash": "sha256:7666d0807d7131796ced32c99da380d3a6db2cf011fd2dcfa7904878799c1871"
21
+ "sourceHash": "sha256:8d6f65f9bdb461e0ce0c6e7fc4850141ca641f9c01e81be60fe826a62ee24669"
22
22
  },
23
23
  "purpose": "Trigger an action. Not for navigation unless `render` supplies a link element.",
24
24
  "props": [
@@ -84,7 +84,9 @@
84
84
  "typeText": "boolean | undefined",
85
85
  "required": false,
86
86
  "default": false,
87
- "origin": "derived"
87
+ "origin": "derived",
88
+ "description": "Fill the width of the container, for forms and narrow screens.",
89
+ "descriptionOrigin": "jsdoc"
88
90
  },
89
91
  {
90
92
  "name": "hidden",
@@ -388,9 +390,9 @@
388
390
  },
389
391
  "examples": [],
390
392
  "coverage": {
391
- "propsTotal": 29,
392
- "propsDescribed": 9,
393
- "describedPct": 31,
393
+ "propsTotal": 10,
394
+ "propsDescribed": 10,
395
+ "describedPct": 100,
394
396
  "authoredSections": [
395
397
  "accessibility",
396
398
  "composition",
@@ -30,6 +30,7 @@ export interface ButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement
30
30
  trailing?: ReactNode;
31
31
  /** An icon-only button. Give it an `aria-label`. */
32
32
  icon?: ReactNode;
33
+ /** Fill the width of the container, for forms and narrow screens. */
33
34
  fullWidth?: boolean;
34
35
  /** Return a promise to show the loading state until it settles. */
35
36
  onClick?(event: MouseEvent<HTMLElement>): unknown;
@@ -12,7 +12,7 @@
12
12
  ]
13
13
  },
14
14
  "source": {
15
- "libraryVersion": "2.4.4",
15
+ "libraryVersion": "2.5.0",
16
16
  "files": [
17
17
  "core/index.ts",
18
18
  "core/trend.ts",
@@ -22,7 +22,7 @@
22
22
  "react/Stat.tsx",
23
23
  "react/props.ts"
24
24
  ],
25
- "sourceHash": "sha256:ef4320f169eb618584b31671b65838e1406de0bf944458079b383dcfd40c2e52"
25
+ "sourceHash": "sha256:5b40d5a873d8c4bd0ea3423f5a9b0ca06f2515aa1ea45149d36be3b2b4aa63e1"
26
26
  },
27
27
  "props": [
28
28
  {
@@ -81,7 +81,9 @@
81
81
  "type": "other",
82
82
  "typeText": "ReactNode",
83
83
  "required": false,
84
- "origin": "derived"
84
+ "origin": "derived",
85
+ "description": "A line under the heading, for context the title cannot carry.",
86
+ "descriptionOrigin": "jsdoc"
85
87
  },
86
88
  {
87
89
  "name": "help",
@@ -113,7 +115,9 @@
113
115
  "type": "other",
114
116
  "typeText": "ReactNode",
115
117
  "required": false,
116
- "origin": "derived"
118
+ "origin": "derived",
119
+ "description": "An icon beside the figure, to identify the metric at a glance.",
120
+ "descriptionOrigin": "jsdoc"
117
121
  },
118
122
  {
119
123
  "name": "id",
@@ -179,7 +183,9 @@
179
183
  "type": "string",
180
184
  "typeText": "string | undefined",
181
185
  "required": false,
182
- "origin": "derived"
186
+ "origin": "derived",
187
+ "description": "Passed through when `href` is set. Worth setting to\n`noopener noreferrer` on any card linking off-site.",
188
+ "descriptionOrigin": "jsdoc"
183
189
  },
184
190
  {
185
191
  "name": "role",
@@ -225,7 +231,9 @@
225
231
  "type": "other",
226
232
  "typeText": "StatTrend | undefined",
227
233
  "required": false,
228
- "origin": "derived"
234
+ "origin": "derived",
235
+ "description": "The change beside the figure: direction, a formatted label, and\nwhether down is the good outcome. Left out, no arrow is shown.",
236
+ "descriptionOrigin": "jsdoc"
229
237
  },
230
238
  {
231
239
  "name": "value",
@@ -327,9 +335,9 @@
327
335
  "safeMutations": {},
328
336
  "examples": [],
329
337
  "coverage": {
330
- "propsTotal": 27,
331
- "propsDescribed": 10,
332
- "describedPct": 37,
338
+ "propsTotal": 16,
339
+ "propsDescribed": 14,
340
+ "describedPct": 88,
333
341
  "authoredSections": [
334
342
  "accessibility"
335
343
  ]
@@ -17,6 +17,10 @@ export interface CardProps extends Omit<HTMLAttributes<HTMLElement>, "title"> {
17
17
  href?: string;
18
18
  /** Passed through when `href` is set. */
19
19
  target?: AnchorHTMLAttributes<HTMLAnchorElement>["target"];
20
+ /**
21
+ * Passed through when `href` is set. Worth setting to
22
+ * `noopener noreferrer` on any card linking off-site.
23
+ */
20
24
  rel?: string;
21
25
  /** Lifts the card on hover, for a card that is clickable by other means. */
22
26
  interactive?: boolean;
@@ -82,6 +86,7 @@ export function Card(props: CardProps) {
82
86
  export interface CardHeaderProps {
83
87
  /** The heading. Wrap it in your own `h2`/`h3` to set the level. */
84
88
  title?: ReactNode;
89
+ /** A line under the heading, for context the title cannot carry. */
85
90
  description?: ReactNode;
86
91
  /** Buttons or a menu, aligned to the end of the header. */
87
92
  actions?: ReactNode;
@@ -23,9 +23,14 @@ export interface StatProps {
23
23
  /** What the figure is. Always visible — a number alone means nothing. */
24
24
  label: ReactNode;
25
25
  value: ReactNode;
26
+ /**
27
+ * The change beside the figure: direction, a formatted label, and
28
+ * whether down is the good outcome. Left out, no arrow is shown.
29
+ */
26
30
  trend?: StatTrend;
27
31
  /** A note under the figure. */
28
32
  help?: ReactNode;
33
+ /** An icon beside the figure, to identify the metric at a glance. */
29
34
  icon?: ReactNode;
30
35
  /** Shows placeholder bars instead of the figure. */
31
36
  loading?: boolean;