@imfusion/web-ui 0.7.2-dev.6.g1f49e9fa → 0.8.1-dev.1.g11d9ffbe

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 (34) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/README.md +32 -0
  3. package/dist/components/account-card/account-card.d.ts +53 -0
  4. package/dist/components/account-card/account-card.meta.d.ts +2 -0
  5. package/dist/components/account-card/index.d.ts +2 -0
  6. package/dist/components/avatar/avatar.d.ts +40 -0
  7. package/dist/components/avatar/avatar.meta.d.ts +2 -0
  8. package/dist/components/avatar/index.d.ts +2 -0
  9. package/dist/components/navigation-menu/subs/link.d.ts +1 -4
  10. package/dist/components/prose/index.d.ts +2 -0
  11. package/dist/components/prose/prose.d.ts +1 -0
  12. package/dist/components/prose/prose.meta.d.ts +2 -0
  13. package/dist/components/separator/separator.d.ts +1 -2
  14. package/dist/components/typo/typo.d.ts +12 -16
  15. package/dist/index.d.ts +3 -0
  16. package/dist/index.js +1358 -1213
  17. package/dist/paths-Oi0v8x-w.js +26 -0
  18. package/dist/style.css +1 -1
  19. package/dist/vite/index.d.ts +1 -0
  20. package/dist/vite/local-web-ui-link.d.ts +8 -0
  21. package/dist/vite.js +32 -17
  22. package/dist/web-ui-cli.js +121 -146
  23. package/docs/user-guide/Changelog.mdx +1 -1
  24. package/package.json +1 -1
  25. package/src/docgen/doc.gen.json +209 -78
  26. package/src/llms/llms.gen.txt +16 -0
  27. package/src/llms/skills/imf-web-ui-conventions/SKILL.md +1 -1
  28. package/src/llms/skills/imf-web-ui-conventions/templates/AUDIT_CHECKLIST.md +5 -4
  29. package/src/llms/skills/imf-web-ui-conventions/topics/library-boundary.md +13 -0
  30. package/src/llms/skills/imf-web-ui-conventions/topics/react.md +1 -1
  31. package/src/llms/skills/imf-web-ui-conventions/topics/styling.md +25 -0
  32. package/src/llms/skills/imf-web-ui-conventions/topics/tooling.md +3 -0
  33. package/src/llms/skills/imf-web-ui-update/SKILL.md +2 -1
  34. package/src/llms/skills/imf-web-ui-ux/SKILL.md +2 -1
@@ -1,4 +1,70 @@
1
1
  {
2
+ "account-card": {
3
+ "root": {
4
+ "name": "AccountCard.Root",
5
+ "description": "AccountCard.Root — the link to the signed-in person's account, holding `AccountCard.Avatar`, `AccountCard.Title`, and\n`AccountCard.Description`. Pass a router link through `render`. `aria-current=\"page\"` marks it current with a tint and\nan inset rule. Its accessible name is the title and description; the avatar stays decorative.\n\nPlace `AccountCard.Action` beside the root, not inside it: a link cannot contain a button.",
6
+ "props": [
7
+ {
8
+ "name": "context",
9
+ "required": false,
10
+ "type": "\"default\" | \"oncolor\" | null",
11
+ "defaultValue": "default",
12
+ "description": ""
13
+ },
14
+ {
15
+ "name": "ref",
16
+ "required": false,
17
+ "type": "Ref<HTMLAnchorElement>",
18
+ "defaultValue": null,
19
+ "description": "Allows getting a ref to the component instance.\nOnce the component unmounts, React will set `ref.current` to `null`\n(or call the ref with `null` if you passed a callback ref).\n@see {@link https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom React Docs}"
20
+ },
21
+ {
22
+ "name": "render",
23
+ "required": false,
24
+ "type": "| ReactElement<\n unknown,\n string | JSXElementConstructor<any>\n >\n| ComponentRenderFn<HTMLProps, {}>",
25
+ "defaultValue": null,
26
+ "description": "Allows you to replace the component's HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
27
+ }
28
+ ]
29
+ },
30
+ "subComponents": [
31
+ {
32
+ "name": "AccountCard.Action",
33
+ "description": "AccountCard.Action — a trailing slot beside `AccountCard.Root`, such as a ghost icon `Button` that logs out. The\nbutton carries its own `aria-label` and its own `context`.",
34
+ "props": []
35
+ },
36
+ {
37
+ "name": "AccountCard.Avatar",
38
+ "description": "AccountCard.Avatar — a decorative `Avatar` in the card's context. Its children are the initials; without them it shows\na person glyph.",
39
+ "props": [
40
+ {
41
+ "name": "size",
42
+ "required": false,
43
+ "type": "\"sm\" | \"md\" | \"lg\" | null",
44
+ "defaultValue": "md",
45
+ "description": "The avatar size."
46
+ },
47
+ {
48
+ "name": "src",
49
+ "required": false,
50
+ "type": "string",
51
+ "defaultValue": null,
52
+ "description": "The person's photo. Until it loads, and when it fails, the initials or the person glyph show instead."
53
+ }
54
+ ]
55
+ },
56
+ {
57
+ "name": "AccountCard.Description",
58
+ "description": "AccountCard.Description — a second line such as organization and role. Truncates to one line.",
59
+ "props": []
60
+ },
61
+ {
62
+ "name": "AccountCard.Title",
63
+ "description": "AccountCard.Title — the account's name, or its email until a name exists. Truncates to one line.",
64
+ "props": []
65
+ }
66
+ ]
67
+ },
2
68
  "app-shell": {
3
69
  "root": {
4
70
  "name": "AppShell.Root",
@@ -62,6 +128,140 @@
62
128
  }
63
129
  ]
64
130
  },
131
+ "avatar": {
132
+ "root": {
133
+ "name": "Avatar.Root",
134
+ "description": "Avatar.Root — a round badge that stands for a person. Also called a profile picture or user badge.\n\nWithout `aria-label` or `aria-labelledby` it is decorative and hidden from assistive tech, for use next to a visible\nname. With a label it is announced as an image with that name.",
135
+ "props": [
136
+ {
137
+ "name": "className",
138
+ "required": false,
139
+ "type": "string | ((state: AvatarRootState) => string)",
140
+ "defaultValue": null,
141
+ "description": "CSS class applied to the element, or a function that\nreturns a class based on the component's state."
142
+ },
143
+ {
144
+ "name": "context",
145
+ "required": false,
146
+ "type": "\"default\" | \"oncolor\" | null",
147
+ "defaultValue": "default",
148
+ "description": ""
149
+ },
150
+ {
151
+ "name": "ref",
152
+ "required": false,
153
+ "type": "Ref<HTMLSpanElement>",
154
+ "defaultValue": null,
155
+ "description": "Allows getting a ref to the component instance.\nOnce the component unmounts, React will set `ref.current` to `null`\n(or call the ref with `null` if you passed a callback ref).\n@see {@link https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom React Docs}"
156
+ },
157
+ {
158
+ "name": "render",
159
+ "required": false,
160
+ "type": "| ReactElement<\n unknown,\n string | JSXElementConstructor<any>\n >\n| ComponentRenderFn<HTMLProps, AvatarRootState>",
161
+ "defaultValue": null,
162
+ "description": "Allows you to replace the component's HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
163
+ },
164
+ {
165
+ "name": "size",
166
+ "required": false,
167
+ "type": "\"sm\" | \"md\" | \"lg\" | null",
168
+ "defaultValue": "md",
169
+ "description": ""
170
+ },
171
+ {
172
+ "name": "style",
173
+ "required": false,
174
+ "type": "| CSSProperties\n| ((state: AvatarRootState) => CSSProperties)",
175
+ "defaultValue": null,
176
+ "description": "Style applied to the element, or a function that\nreturns a style object based on the component's state."
177
+ }
178
+ ]
179
+ },
180
+ "subComponents": [
181
+ {
182
+ "name": "Avatar.Fallback",
183
+ "description": "Avatar.Fallback — the initials the consumer passes, one or two letters. Without children it shows a person glyph.\nIt derives nothing from a name or email.",
184
+ "props": [
185
+ {
186
+ "name": "className",
187
+ "required": false,
188
+ "type": "string | ((state: AvatarFallbackState) => string)",
189
+ "defaultValue": null,
190
+ "description": "CSS class applied to the element, or a function that\nreturns a class based on the component's state."
191
+ },
192
+ {
193
+ "name": "delay",
194
+ "required": false,
195
+ "type": "number",
196
+ "defaultValue": null,
197
+ "description": "How long to wait before showing the fallback. Specified in milliseconds."
198
+ },
199
+ {
200
+ "name": "ref",
201
+ "required": false,
202
+ "type": "Ref<HTMLSpanElement>",
203
+ "defaultValue": null,
204
+ "description": "Allows getting a ref to the component instance.\nOnce the component unmounts, React will set `ref.current` to `null`\n(or call the ref with `null` if you passed a callback ref).\n@see {@link https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom React Docs}"
205
+ },
206
+ {
207
+ "name": "render",
208
+ "required": false,
209
+ "type": "| ReactElement<\n unknown,\n string | JSXElementConstructor<any>\n >\n| ComponentRenderFn<HTMLProps, AvatarFallbackState>",
210
+ "defaultValue": null,
211
+ "description": "Allows you to replace the component's HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
212
+ },
213
+ {
214
+ "name": "style",
215
+ "required": false,
216
+ "type": "| CSSProperties\n| ((state: AvatarFallbackState) => CSSProperties)",
217
+ "defaultValue": null,
218
+ "description": "Style applied to the element, or a function that\nreturns a style object based on the component's state."
219
+ }
220
+ ]
221
+ },
222
+ {
223
+ "name": "Avatar.Image",
224
+ "description": "Avatar.Image — the person's photo, cropped to the circle. It shows once `src` has loaded; until then, and when it\nfails, `Avatar.Fallback` shows instead.",
225
+ "props": [
226
+ {
227
+ "name": "className",
228
+ "required": false,
229
+ "type": "string | ((state: AvatarImageState) => string)",
230
+ "defaultValue": null,
231
+ "description": "CSS class applied to the element, or a function that\nreturns a class based on the component's state."
232
+ },
233
+ {
234
+ "name": "onLoadingStatusChange",
235
+ "required": false,
236
+ "type": "(status: ImageLoadingStatus) => void",
237
+ "defaultValue": null,
238
+ "description": "Callback fired when the loading status changes."
239
+ },
240
+ {
241
+ "name": "ref",
242
+ "required": false,
243
+ "type": "Ref<HTMLSpanElement>",
244
+ "defaultValue": null,
245
+ "description": "Allows getting a ref to the component instance.\nOnce the component unmounts, React will set `ref.current` to `null`\n(or call the ref with `null` if you passed a callback ref).\n@see {@link https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom React Docs}"
246
+ },
247
+ {
248
+ "name": "render",
249
+ "required": false,
250
+ "type": "| ReactElement<\n unknown,\n string | JSXElementConstructor<any>\n >\n| ComponentRenderFn<HTMLProps, AvatarImageState>",
251
+ "defaultValue": null,
252
+ "description": "Allows you to replace the component's HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
253
+ },
254
+ {
255
+ "name": "style",
256
+ "required": false,
257
+ "type": "| CSSProperties\n| ((state: AvatarImageState) => CSSProperties)",
258
+ "defaultValue": null,
259
+ "description": "Style applied to the element, or a function that\nreturns a style object based on the component's state."
260
+ }
261
+ ]
262
+ }
263
+ ]
264
+ },
65
265
  "button": {
66
266
  "root": {
67
267
  "name": "Button",
@@ -2461,7 +2661,7 @@
2461
2661
  },
2462
2662
  {
2463
2663
  "name": "NavigationMenu.Link",
2464
- "description": "NavigationMenu.Link — a top-strip (rail) link: a peer of `Trigger` for a\nsection that navigates directly instead of opening a flyout. Renders an `<a>`.\nFor a link inside a flyout, use `NavigationMenu.FlyoutLink`.\n\nA trailing `ArrowRight` slides in on hover/focus. When a custom `render` is\nsupplied the icon is omitted — the consumer's element owns its content.",
2664
+ "description": "NavigationMenu.Link — a top-strip (rail) link: a peer of `Trigger` for a\nsection that navigates directly instead of opening a flyout. Renders an `<a>`.\nFor a link inside a flyout, use `NavigationMenu.FlyoutLink`.",
2465
2665
  "props": [
2466
2666
  {
2467
2667
  "name": "active",
@@ -4250,6 +4450,14 @@
4250
4450
  }
4251
4451
  ]
4252
4452
  },
4453
+ "prose": {
4454
+ "root": {
4455
+ "name": "Prose",
4456
+ "description": "",
4457
+ "props": []
4458
+ },
4459
+ "subComponents": []
4460
+ },
4253
4461
  "row": {
4254
4462
  "root": {
4255
4463
  "name": "Row",
@@ -5306,13 +5514,6 @@
5306
5514
  "defaultValue": null,
5307
5515
  "description": "Allows you to replace the component's HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
5308
5516
  },
5309
- {
5310
- "name": "spacing",
5311
- "required": false,
5312
- "type": "| \"small\"\n| \"none\"\n| \"medium\"\n| \"large\"\n| \"spacious\"\n| null",
5313
- "defaultValue": "none",
5314
- "description": ""
5315
- },
5316
5517
  {
5317
5518
  "name": "style",
5318
5519
  "required": false,
@@ -7559,13 +7760,6 @@
7559
7760
  "defaultValue": null,
7560
7761
  "description": "Allows you to replace the component's HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
7561
7762
  },
7562
- {
7563
- "name": "spacing",
7564
- "required": false,
7565
- "type": "\"default\" | \"none\" | null",
7566
- "defaultValue": "default",
7567
- "description": ""
7568
- },
7569
7763
  {
7570
7764
  "name": "variant",
7571
7765
  "required": false,
@@ -7627,13 +7821,6 @@
7627
7821
  "defaultValue": null,
7628
7822
  "description": "Allows you to replace the component's HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
7629
7823
  },
7630
- {
7631
- "name": "spacing",
7632
- "required": false,
7633
- "type": "\"default\" | \"none\" | null",
7634
- "defaultValue": "default",
7635
- "description": ""
7636
- },
7637
7824
  {
7638
7825
  "name": "variant",
7639
7826
  "required": false,
@@ -7668,13 +7855,6 @@
7668
7855
  "defaultValue": null,
7669
7856
  "description": "Allows you to replace the component's HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
7670
7857
  },
7671
- {
7672
- "name": "spacing",
7673
- "required": false,
7674
- "type": "\"default\" | \"none\" | null",
7675
- "defaultValue": "default",
7676
- "description": ""
7677
- },
7678
7858
  {
7679
7859
  "name": "variant",
7680
7860
  "required": false,
@@ -7702,13 +7882,6 @@
7702
7882
  "defaultValue": null,
7703
7883
  "description": "Allows you to replace the component's HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
7704
7884
  },
7705
- {
7706
- "name": "spacing",
7707
- "required": false,
7708
- "type": "\"default\" | \"none\" | null",
7709
- "defaultValue": "default",
7710
- "description": ""
7711
- },
7712
7885
  {
7713
7886
  "name": "variant",
7714
7887
  "required": false,
@@ -7736,13 +7909,6 @@
7736
7909
  "defaultValue": null,
7737
7910
  "description": "Allows you to replace the component's HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
7738
7911
  },
7739
- {
7740
- "name": "spacing",
7741
- "required": false,
7742
- "type": "\"default\" | \"none\" | null",
7743
- "defaultValue": "default",
7744
- "description": ""
7745
- },
7746
7912
  {
7747
7913
  "name": "variant",
7748
7914
  "required": false,
@@ -7777,13 +7943,6 @@
7777
7943
  "defaultValue": null,
7778
7944
  "description": "Allows you to replace the component's HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
7779
7945
  },
7780
- {
7781
- "name": "spacing",
7782
- "required": false,
7783
- "type": "\"default\" | \"none\" | null",
7784
- "defaultValue": "default",
7785
- "description": ""
7786
- },
7787
7946
  {
7788
7947
  "name": "variant",
7789
7948
  "required": false,
@@ -7872,13 +8031,6 @@
7872
8031
  "defaultValue": null,
7873
8032
  "description": "Allows you to replace the component's HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
7874
8033
  },
7875
- {
7876
- "name": "spacing",
7877
- "required": false,
7878
- "type": "\"default\" | \"none\" | null",
7879
- "defaultValue": "default",
7880
- "description": ""
7881
- },
7882
8034
  {
7883
8035
  "name": "variant",
7884
8036
  "required": false,
@@ -7940,13 +8092,6 @@
7940
8092
  "defaultValue": null,
7941
8093
  "description": "Allows you to replace the component's HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
7942
8094
  },
7943
- {
7944
- "name": "spacing",
7945
- "required": false,
7946
- "type": "\"default\" | \"none\" | null",
7947
- "defaultValue": "default",
7948
- "description": ""
7949
- },
7950
8095
  {
7951
8096
  "name": "variant",
7952
8097
  "required": false,
@@ -7974,13 +8119,6 @@
7974
8119
  "defaultValue": null,
7975
8120
  "description": "Allows you to replace the component's HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
7976
8121
  },
7977
- {
7978
- "name": "spacing",
7979
- "required": false,
7980
- "type": "\"default\" | \"none\" | null",
7981
- "defaultValue": "default",
7982
- "description": ""
7983
- },
7984
8122
  {
7985
8123
  "name": "variant",
7986
8124
  "required": false,
@@ -8035,13 +8173,6 @@
8035
8173
  "defaultValue": null,
8036
8174
  "description": "Allows you to replace the component's HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
8037
8175
  },
8038
- {
8039
- "name": "spacing",
8040
- "required": false,
8041
- "type": "\"default\" | \"none\" | null",
8042
- "defaultValue": "default",
8043
- "description": ""
8044
- },
8045
8176
  {
8046
8177
  "name": "variant",
8047
8178
  "required": false,
@@ -9,11 +9,22 @@ reading the whole file. `further reading` links (when present) point at
9
9
  the upstream library's own docs for usage/anatomy/composition — not for
10
10
  props, which always live in the docgen file above.
11
11
 
12
+ ## AccountCard
13
+ - category: Display, status: experimental
14
+ Link to the signed-in person's account, with avatar, name or email, and a second line such as organization and role. Also called an ID card or user row; for a bare person badge, use Avatar.
15
+ - props: node_modules/@imfusion/web-ui/src/docgen/doc.gen.json -> .account-card (jq: jq '.account-card' node_modules/@imfusion/web-ui/src/docgen/doc.gen.json)
16
+
12
17
  ## AppShell
13
18
  - category: Layout, status: stable
14
19
  Application layout shell for a header, sidebars, footer, and scrolling main area. Also called an app layout or dashboard shell; sizing is CSS-driven and sidebars can become sliding panels on small screens.
15
20
  - props: node_modules/@imfusion/web-ui/src/docgen/doc.gen.json -> .app-shell (jq: jq '.app-shell' node_modules/@imfusion/web-ui/src/docgen/doc.gen.json)
16
21
 
22
+ ## Avatar
23
+ - category: Display, status: experimental
24
+ Round badge that stands for a person, showing their photo, their initials, or a person glyph. Also called a profile picture or user badge; for a signed-in account row with a name, use AccountCard.
25
+ - props: node_modules/@imfusion/web-ui/src/docgen/doc.gen.json -> .avatar (jq: jq '.avatar' node_modules/@imfusion/web-ui/src/docgen/doc.gen.json)
26
+ - further reading (usage/anatomy, not props): https://base-ui.com/react/components/avatar.md
27
+
17
28
  ## Button
18
29
  - category: Buttons, status: stable
19
30
  Triggers an action such as submit, confirm, cancel, navigate, or delete. Also called a CTA or action button; variants and sizes communicate its importance.
@@ -125,6 +136,11 @@ Floating panel anchored to a trigger for contextual content, quick forms, or act
125
136
  - props: node_modules/@imfusion/web-ui/src/docgen/doc.gen.json -> .popover (jq: jq '.popover' node_modules/@imfusion/web-ui/src/docgen/doc.gen.json)
126
137
  - further reading (usage/anatomy, not props): https://base-ui.com/react/components/popover.md
127
138
 
139
+ ## Prose
140
+ - category: Layout, status: experimental
141
+ Flow container that spaces long-form text and the blocks inside it. Also called an article or rich-text container. Use Stack or Row for UI layout.
142
+ - props: node_modules/@imfusion/web-ui/src/docgen/doc.gen.json -> .prose (jq: jq '.prose' node_modules/@imfusion/web-ui/src/docgen/doc.gen.json)
143
+
128
144
  ## Row
129
145
  - category: Layout, status: stable
130
146
  Horizontal layout primitive for spacing, aligning, and wrapping children. Also called a horizontal stack or flex row.
@@ -17,7 +17,7 @@ a gap or to make a deliberate deviation visible; do not refactor a project just
17
17
  | Topic | Read when |
18
18
  | ------------------------------------------------ | --------------------------------------------------------------- |
19
19
  | [library-setup](topics/library-setup.md) | Installing or repairing stylesheet and provider wiring. |
20
- | [library-boundary](topics/library-boundary.md) | Rendering Web UI components, wrapping them, or choosing a peer. |
20
+ | [library-boundary](topics/library-boundary.md) | Rendering, locking, or wrapping components, or choosing a peer. |
21
21
  | [authentication](topics/authentication.md) | Protecting routes or defining login and logout. |
22
22
  | [react](topics/react.md) | Designing component roles, state ownership, or effects. |
23
23
  | [components](topics/components.md) | Adding files or deciding component anatomy. |
@@ -13,6 +13,7 @@ sections one to one — change a topic's sections and this file follows in the s
13
13
 
14
14
  - [ ] Stay behind the library
15
15
  - [ ] Wrap primitives when the app has a reason to
16
+ - [ ] Lock a field: read-only or disabled
16
17
  - [ ] Derive types, don't import them
17
18
  - [ ] Integrations own their peers
18
19
  - [ ] Styling crosses the boundary through seams
@@ -56,6 +57,7 @@ sections one to one — change a topic's sections and this file follows in the s
56
57
  - [ ] CSS authoring
57
58
  - [ ] Build custom UI from tokens
58
59
  - [ ] Override through the sanctioned seams
60
+ - [ ] Spacing
59
61
  - [ ] The color system
60
62
  - [ ] Browser floor
61
63
  - [ ] Responsive styling
@@ -101,10 +103,9 @@ sections one to one — change a topic's sections and this file follows in the s
101
103
  - [ ] Devtools
102
104
  - [ ] Docs over memory
103
105
  - [ ] Prettier
104
- - [ ] ESLint
105
- - [ ] tsconfig
106
- - [ ] Staged files
107
- - [ ] CSS class names
106
+ - [ ] ESLint and TypeScript
107
+ - [ ] Pre-commit checks
108
+ - [ ] Vite plugins
108
109
 
109
110
  ## npm project — `npm-project`
110
111
 
@@ -28,6 +28,19 @@ Module classes.
28
28
  An `experimental` primitive should be wrapped once before it is used throughout an app. That gives an API change one place to
29
29
  land.
30
30
 
31
+ ## Lock a field: read-only or disabled
32
+
33
+ Ask whether the value is final here or the control is unavailable right now.
34
+
35
+ - `readOnly`: the value is real data the user cannot change in this context, such as an invited email or a generated ID. It
36
+ stays focusable, selectable, and copyable, screen readers announce it as read only, and the form submits it.
37
+ - `disabled`: the control does not apply until something on this screen changes, such as a field that waits for another
38
+ choice or a form that is saving. It leaves the tab order and the form data. Show the reason next to it.
39
+ - Plain text: nothing in the view is ever editable. Render a label and the value instead of a locked input.
40
+
41
+ Set one of the two. When both apply, for example a read-only field inside a disabled `Fieldset`, disabled wins in behavior
42
+ and in the library's styling. Re-derive a read-only value on the server instead of trusting the submitted copy.
43
+
31
44
  ## Derive types, don't copy them
32
45
 
33
46
  The library intentionally does not export component `Props` interfaces. Use `React.ComponentProps<typeof Component>` and
@@ -9,7 +9,7 @@ looks.
9
9
  component CSS.
10
10
  - **Partials** are reusable compositions extracted because they read as a unit. They have light logic and no styling of their
11
11
  own.
12
- - **Layout components** arrange content with `Stack`, `Row`, grids, and token gaps.
12
+ - **Layout components** arrange content with `Stack`, `Row`, grids, and token gaps, and wrap long-form text in `Prose`.
13
13
  - **Dumb components** receive data and callbacks, render UI, and own their styles. They do not fetch, route, or contain
14
14
  business rules.
15
15
 
@@ -50,6 +50,31 @@ hook, not a state flag.
50
50
  }
51
51
  ```
52
52
 
53
+ ## Spacing
54
+
55
+ Web UI components have no outer margin. The parent sets the distance between its children: `Stack` and `Row` gaps in UI,
56
+ `Prose` for long-form text. Inside a flex or grid gap a margin adds to the gap instead of collapsing, so do not add one to a
57
+ Web UI component.
58
+
59
+ ```tsx
60
+ // Wrong: the margin adds to the Stack gap.
61
+ <Stack gap="3">
62
+ <Typo.P style={{ marginBlockEnd: "1em" }}>Your study is ready.</Typo.P>
63
+ <Callout.Root>…</Callout.Root>
64
+ </Stack>
65
+
66
+ // Right: UI spaced by the gap, text spaced by Prose.
67
+ <Stack gap="3">
68
+ <Typo.P>Your study is ready.</Typo.P>
69
+ <Callout.Root>…</Callout.Root>
70
+ </Stack>
71
+
72
+ <Prose>
73
+ <Typo.H2>Export</Typo.H2>
74
+ <Typo.P>Pick a format, then a destination.</Typo.P>
75
+ </Prose>
76
+ ```
77
+
53
78
  ## The color system
54
79
 
55
80
  Use surface roles (`main`, `support`, `minor`), the separate `brand` and `primary` roles, status roles, and the three accent
@@ -88,3 +88,6 @@ export default defineConfig({
88
88
  `imfusionBrandAssets` serves the complete favicon set during development and emits it at stable root URLs during builds.
89
89
  Register `readableCssModuleNames` in every compiler that processes the app's CSS, including development, Storybook, and
90
90
  production. A compiler left out produces different class names and styles that appear to work only in some environments.
91
+
92
+ `localWebUILink` dedupes the library's peer dependencies when `@imfusion/web-ui` is linked from a local checkout
93
+ (`file:../web-ui`), so the app loads one React. It does nothing for an installed package and does not cover server rendering.
@@ -108,7 +108,8 @@ This phase reads and reports: every file stays as it is until the person approve
108
108
  releases read, then report separately:
109
109
 
110
110
  - migrations: every `Breaking` entry and migration step, mapped to the call sites it affects here and the fix the changelog
111
- shows; a step with no matching code is reported as not applicable;
111
+ shows; a step with no matching code is reported as not applicable. A style-only change, such as a removed margin, raises no
112
+ type error: find its call sites by component usage;
112
113
  - compatibility fixes required by changed imports or types that the changelog does not cover;
113
114
  - existing uses affected by a changed API or documented behavior;
114
115
  - suggestions: new features from the highlights and `Added` entries that fit what the project already builds, each with the
@@ -54,7 +54,8 @@ reason.
54
54
  ## Build the hierarchy
55
55
 
56
56
  - Frame an application with `AppShell`.
57
- - Compose `Stack` and `Row` for spacing and arrangement. Use `Separator` for a real division.
57
+ - Compose `Stack` and `Row` for spacing and arrangement, and wrap long-form text in `Prose`. Use `Separator` for a real
58
+ division.
58
59
  - Use `Typo` for text hierarchy instead of styling raw headings beside it.
59
60
  - Give a view one primary action. Make other actions quieter.
60
61
  - Choose density from the user's frequency and data volume: frequent work and large data need compact layouts; occasional