@estiva-app/ui 0.37.1 → 0.38.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 (52) hide show
  1. package/dist/AttachmentCard.d.ts.map +1 -1
  2. package/dist/Kbd.d.ts +7 -1
  3. package/dist/Kbd.d.ts.map +1 -1
  4. package/dist/Lightbox.d.ts.map +1 -1
  5. package/dist/SectionLabel.d.ts.map +1 -1
  6. package/dist/Toast.d.ts.map +1 -1
  7. package/dist/cn.d.ts.map +1 -1
  8. package/dist/eslint/index.d.ts +2 -4
  9. package/dist/eslint/index.d.ts.map +1 -1
  10. package/dist/eslint/index.js +14 -2
  11. package/dist/eslint/index.js.map +3 -3
  12. package/dist/eslint/no-restyled-part.d.ts.map +1 -1
  13. package/dist/eslint/rule-ids.d.ts +18 -0
  14. package/dist/eslint/rule-ids.d.ts.map +1 -0
  15. package/dist/gates/{chunk-CC6L4B5U.js → chunk-DZVDOJGV.js} +16 -2
  16. package/dist/gates/chunk-DZVDOJGV.js.map +7 -0
  17. package/dist/gates/create-app.js +1 -1
  18. package/dist/gates/index.js +1 -1
  19. package/dist/index.js +23 -19
  20. package/dist/index.js.map +2 -2
  21. package/dist/looks.d.ts +2 -2
  22. package/dist/looks.d.ts.map +1 -1
  23. package/package.json +2 -1
  24. package/registry.json +55 -38
  25. package/src/AppShell.stories.tsx +4 -12
  26. package/src/AttachmentCard.tsx +0 -1
  27. package/src/CommandPalette.tsx +1 -1
  28. package/src/ErrorBoundary.mdx +2 -2
  29. package/src/Kbd.tsx +10 -1
  30. package/src/Lightbox.tsx +4 -2
  31. package/src/Menu.tsx +1 -1
  32. package/src/MenuItem.stories.tsx +4 -9
  33. package/src/Panel.stories.tsx +1 -1
  34. package/src/Popover.stories.tsx +3 -0
  35. package/src/SectionLabel.tsx +2 -0
  36. package/src/Select.tsx +2 -2
  37. package/src/TextInput.test.tsx +1 -1
  38. package/src/Toast.tsx +7 -6
  39. package/src/Tooltip.stories.tsx +9 -9
  40. package/src/Tooltip.test.tsx +1 -1
  41. package/src/Tooltip.tsx +3 -3
  42. package/src/cn.ts +1 -1
  43. package/src/eslint/index.ts +6 -6
  44. package/src/eslint/no-restyled-part.ts +1 -0
  45. package/src/eslint/rule-ids.ts +29 -0
  46. package/src/gates/create-app.test.ts +46 -0
  47. package/src/gates/create-app.ts +1 -1
  48. package/src/looks.ts +2 -2
  49. package/stories/TokensPage.tsx +17 -3
  50. package/tailwind-preset.js +10 -0
  51. package/tokens.css +144 -0
  52. package/dist/gates/chunk-CC6L4B5U.js.map +0 -7
package/dist/looks.d.ts CHANGED
@@ -11,7 +11,7 @@
11
11
  * much room it takes stay in the part.
12
12
  */
13
13
  /** A field's box: TextInput and Textarea. The hairline strengthens on hover; focus comes after it, so a focused field keeps its colour under the pointer. */
14
- export declare const FIELD_BOX_CLASSES = "bg-bg-inset border border-border-default hover:border-border-strong focus:border-border-focus rounded-lg";
14
+ export declare const FIELD_BOX_CLASSES = "bg-bg-field border border-border-field shadow-field hover:border-border-strong focus:border-border-focus rounded-lg";
15
15
  /** What a field's words look like, typed or waiting: TextInput, Textarea and Select. */
16
16
  export declare const FIELD_TEXT_CLASSES = "text-text-primary placeholder:text-text-muted";
17
17
  /** A field that cannot be used: TextInput, Textarea and Select. */
@@ -24,7 +24,7 @@ export declare const FIELD_SIZE_CLASSES: {
24
24
  readonly small: "h-6 min-h-6 px-2 text-caption";
25
25
  };
26
26
  /** The box around an input that sits inside it, before its focus look: SearchInput and ChipInput. */
27
- export declare const FIELD_SHELL_CLASSES = "bg-bg-inset border border-border-default hover:border-border-strong rounded-lg transition-colors";
27
+ export declare const FIELD_SHELL_CLASSES = "bg-bg-field border border-border-field shadow-field hover:border-border-strong rounded-lg transition-colors";
28
28
  /** A panel that floats over the page: Menu's panel and Tooltip. */
29
29
  export declare const FLOATING_SURFACE_CLASSES = "rounded-lg border border-border-default bg-bg-elevated shadow-lg";
30
30
  /** A chip's words: Chip's label and Reaction's count. */
@@ -1 +1 @@
1
- {"version":3,"file":"looks.d.ts","sourceRoot":"","sources":["../src/looks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,6JAA6J;AAC7J,eAAO,MAAM,iBAAiB,6GAA6G,CAAA;AAE3I,wFAAwF;AACxF,eAAO,MAAM,kBAAkB,kDAAkD,CAAA;AAEjF,mEAAmE;AACnE,eAAO,MAAM,sBAAsB,qFAAqF,CAAA;AAExH,wEAAwE;AACxE,eAAO,MAAM,wBAAwB,4DAA4D,CAAA;AAEjG,kJAAkJ;AAClJ,eAAO,MAAM,kBAAkB;;;CAGrB,CAAA;AAEV,qGAAqG;AACrG,eAAO,MAAM,mBAAmB,qGAAqG,CAAA;AAErI,mEAAmE;AACnE,eAAO,MAAM,wBAAwB,qEAAqE,CAAA;AAE1G,yDAAyD;AACzD,eAAO,MAAM,iBAAiB,0FAA0F,CAAA;AAExH,4FAA4F;AAC5F,eAAO,MAAM,kBAAkB,8EAA8E,CAAA"}
1
+ {"version":3,"file":"looks.d.ts","sourceRoot":"","sources":["../src/looks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,6JAA6J;AAC7J,eAAO,MAAM,iBAAiB,wHAAwH,CAAA;AAEtJ,wFAAwF;AACxF,eAAO,MAAM,kBAAkB,kDAAkD,CAAA;AAEjF,mEAAmE;AACnE,eAAO,MAAM,sBAAsB,qFAAqF,CAAA;AAExH,wEAAwE;AACxE,eAAO,MAAM,wBAAwB,4DAA4D,CAAA;AAEjG,kJAAkJ;AAClJ,eAAO,MAAM,kBAAkB;;;CAGrB,CAAA;AAEV,qGAAqG;AACrG,eAAO,MAAM,mBAAmB,gHAAgH,CAAA;AAEhJ,mEAAmE;AACnE,eAAO,MAAM,wBAAwB,qEAAqE,CAAA;AAE1G,yDAAyD;AACzD,eAAO,MAAM,iBAAiB,0FAA0F,CAAA;AAExH,4FAA4F;AAC5F,eAAO,MAAM,kBAAkB,8EAA8E,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@estiva-app/ui",
3
- "version": "0.37.1",
3
+ "version": "0.38.0",
4
4
  "description": "Estiva's design tokens (the contract) and a small set of primitives (a convenience) for every Estiva app.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -53,6 +53,7 @@
53
53
  "test:watch": "vitest --project unit",
54
54
  "test:a11y": "vitest run --project storybook-signal --project storybook-ship",
55
55
  "test:stories": "vitest run --project storybook-draw",
56
+ "test:docs": "node scripts/docs-pages-draw.mjs",
56
57
  "typecheck": "tsc --noEmit",
57
58
  "prelint": "node build.mjs",
58
59
  "lint": "eslint .",
package/registry.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "kind": "package",
5
5
  "repo": "estiva-ui",
6
6
  "package": "@estiva-app/ui",
7
- "packageVersion": "0.37.1",
7
+ "packageVersion": "0.38.0",
8
8
  "exports": 88,
9
9
  "typeExports": 97,
10
10
  "files": null
@@ -952,7 +952,7 @@
952
952
  "app": null,
953
953
  "looks": [
954
954
  "bg-bg-elevated border border-border-subtle rounded-md",
955
- "bg-bg-inset border border-border-default hover:border-border-strong rounded-lg transition-colors focus-within:border-border-focus focus-within:hover:border-border-focus cursor-text signal:transition-shadow signal:focus-within:shadow-focus-ring",
955
+ "bg-bg-field border border-border-field shadow-field hover:border-border-strong rounded-lg transition-colors focus-within:border-border-focus focus-within:hover:border-border-focus cursor-text signal:transition-shadow signal:focus-within:shadow-focus-ring",
956
956
  "focus-within:border-border-focus focus-within:hover:border-border-focus cursor-text signal:transition-shadow signal:focus-within:shadow-focus-ring",
957
957
  "text-body-2 border-none bg-transparent text-text-primary placeholder:text-text-muted outline-none"
958
958
  ]
@@ -1889,7 +1889,7 @@
1889
1889
  "docPage": null,
1890
1890
  "app": null,
1891
1891
  "looks": [
1892
- "text-menu signal:font-mono signal:text-small signal:tracking-wider"
1892
+ "text-menu signal:font-mono signal:text-small signal:tracking-wider leaf:font-mono leaf:text-small leaf:tracking-wider"
1893
1893
  ]
1894
1894
  },
1895
1895
  {
@@ -2607,9 +2607,23 @@
2607
2607
  "takes": "text",
2608
2608
  "required": false,
2609
2609
  "note": null
2610
+ },
2611
+ {
2612
+ "name": "tone",
2613
+ "takes": "default | tooltip",
2614
+ "required": false,
2615
+ "note": "`tooltip` draws the key in the tooltip's own key colours, for a theme whose tooltip is darker than its page (Katerina, 27 September)."
2616
+ }
2617
+ ],
2618
+ "variants": [
2619
+ {
2620
+ "prop": "tone",
2621
+ "values": [
2622
+ "default",
2623
+ "tooltip"
2624
+ ]
2610
2625
  }
2611
2626
  ],
2612
- "variants": [],
2613
2627
  "ownsBehaviours": [],
2614
2628
  "status": "stable",
2615
2629
  "migrationStage": null,
@@ -2618,10 +2632,12 @@
2618
2632
  "docPage": "src/Kbd.mdx",
2619
2633
  "app": null,
2620
2634
  "looks": [
2621
- "whitespace-nowrap rounded-sm border border-border-strong bg-bg-inset font-sans text-caption text-text-secondary signal:border-b-2 signal:bg-bg-wash signal:font-mono signal:text-small ship:border-b-2 ship:bg-bg-wash ship:font-mono ship:text-small",
2635
+ "whitespace-nowrap rounded-sm border border-border-strong bg-bg-inset font-sans text-caption text-text-secondary signal:border-b-2 signal:bg-bg-wash signal:font-mono signal:text-small ship:border-b-2 ship:bg-bg-wash ship:font-mono ship:text-small leaf:font-mono leaf:text-small",
2636
+ "whitespace-nowrap rounded-sm border border-border-strong bg-bg-inset font-sans text-caption text-text-secondary signal:border-b-2 signal:bg-bg-wash signal:font-mono signal:text-small ship:border-b-2 ship:bg-bg-wash ship:font-mono ship:text-small leaf:font-mono leaf:text-small border-border-tooltip-key bg-bg-tooltip-key text-text-tooltip-key signal:bg-bg-tooltip-key ship:bg-bg-tooltip-key",
2622
2637
  "whitespace-nowrap rounded-sm border border-border-strong bg-bg-inset font-sans text-caption text-text-secondary",
2623
2638
  "signal:border-b-2 signal:bg-bg-wash signal:font-mono signal:text-small",
2624
- "ship:border-b-2 ship:bg-bg-wash ship:font-mono ship:text-small"
2639
+ "ship:border-b-2 ship:bg-bg-wash ship:font-mono ship:text-small",
2640
+ "border-border-tooltip-key bg-bg-tooltip-key text-text-tooltip-key signal:bg-bg-tooltip-key ship:bg-bg-tooltip-key"
2625
2641
  ]
2626
2642
  },
2627
2643
  {
@@ -2696,7 +2712,7 @@
2696
2712
  "docPage": "src/Lightbox.mdx",
2697
2713
  "app": null,
2698
2714
  "looks": [
2699
- "rounded-full bg-bg-elevated border border-border-strong text-text-secondary hover:text-text-primary"
2715
+ "rounded-full bg-bg-tooltip border border-border-tooltip-key text-text-tooltip-key hover:text-text-tooltip"
2700
2716
  ]
2701
2717
  },
2702
2718
  {
@@ -4184,7 +4200,7 @@
4184
4200
  "docPage": "src/SearchInput.mdx",
4185
4201
  "app": null,
4186
4202
  "looks": [
4187
- "bg-bg-inset border border-border-default hover:border-border-strong rounded-lg transition-colors focus-within:border-border-strong",
4203
+ "bg-bg-field border border-border-field shadow-field hover:border-border-strong rounded-lg transition-colors focus-within:border-border-strong",
4188
4204
  "text-input-value bg-transparent text-text-primary placeholder:text-text-muted outline-none"
4189
4205
  ]
4190
4206
  },
@@ -4339,13 +4355,14 @@
4339
4355
  "docPage": "src/SectionLabel.mdx",
4340
4356
  "app": null,
4341
4357
  "looks": [
4342
- "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest text-text-secondary",
4343
- "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest text-text-secondary truncate",
4344
- "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest text-text-muted",
4345
- "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest text-text-muted truncate",
4346
- "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest text-text-primary",
4347
- "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest text-text-primary truncate",
4348
- "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest"
4358
+ "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest text-text-secondary",
4359
+ "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest text-text-secondary truncate",
4360
+ "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest text-text-muted",
4361
+ "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest text-text-muted truncate",
4362
+ "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest text-text-primary",
4363
+ "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest text-text-primary truncate",
4364
+ "text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest",
4365
+ "leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest"
4349
4366
  ]
4350
4367
  },
4351
4368
  {
@@ -4468,15 +4485,15 @@
4468
4485
  "docPage": "src/Select.mdx",
4469
4486
  "app": null,
4470
4487
  "looks": [
4471
- "rounded-lg border bg-bg-inset text-left border-border-default outline-none transition-colors text-text-primary placeholder:text-text-muted disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled focus-visible:border-border-focus aria-expanded:border-border-focus signal:transition-shadow signal:focus-visible:shadow-focus-ring text-input-value",
4472
- "rounded-lg border bg-bg-inset text-left border-border-default outline-none transition-colors text-text-primary placeholder:text-text-muted disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled focus-visible:border-border-focus aria-expanded:border-border-focus signal:transition-shadow signal:focus-visible:shadow-focus-ring text-caption",
4473
- "rounded-lg border bg-bg-inset text-left border-border-default outline-none transition-colors text-text-primary placeholder:text-text-muted disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled cursor-not-allowed bg-bg-disabled text-text-disabled focus-visible:border-border-focus aria-expanded:border-border-focus signal:transition-shadow signal:focus-visible:shadow-focus-ring text-input-value",
4474
- "rounded-lg border bg-bg-inset text-left border-border-default outline-none transition-colors text-text-primary placeholder:text-text-muted disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled cursor-not-allowed bg-bg-disabled text-text-disabled focus-visible:border-border-focus aria-expanded:border-border-focus signal:transition-shadow signal:focus-visible:shadow-focus-ring text-caption",
4475
- "rounded-lg border bg-bg-inset text-left border-border-default outline-none transition-colors text-text-primary placeholder:text-text-muted hover:border-border-strong focus-visible:hover:border-border-focus aria-expanded:hover:border-border-focus disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled focus-visible:border-border-focus aria-expanded:border-border-focus signal:transition-shadow signal:focus-visible:shadow-focus-ring text-input-value",
4476
- "rounded-lg border bg-bg-inset text-left border-border-default outline-none transition-colors text-text-primary placeholder:text-text-muted hover:border-border-strong focus-visible:hover:border-border-focus aria-expanded:hover:border-border-focus disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled focus-visible:border-border-focus aria-expanded:border-border-focus signal:transition-shadow signal:focus-visible:shadow-focus-ring text-caption",
4477
- "rounded-lg border bg-bg-inset text-left border-border-default outline-none transition-colors text-text-primary placeholder:text-text-muted hover:border-border-strong focus-visible:hover:border-border-focus aria-expanded:hover:border-border-focus disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled cursor-not-allowed bg-bg-disabled text-text-disabled focus-visible:border-border-focus aria-expanded:border-border-focus signal:transition-shadow signal:focus-visible:shadow-focus-ring text-input-value",
4478
- "rounded-lg border bg-bg-inset text-left border-border-default outline-none transition-colors text-text-primary placeholder:text-text-muted hover:border-border-strong focus-visible:hover:border-border-focus aria-expanded:hover:border-border-focus disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled cursor-not-allowed bg-bg-disabled text-text-disabled focus-visible:border-border-focus aria-expanded:border-border-focus signal:transition-shadow signal:focus-visible:shadow-focus-ring text-caption",
4479
- "rounded-lg border bg-bg-inset text-left"
4488
+ "rounded-lg border bg-bg-field shadow-field text-left border-border-field outline-none transition-colors text-text-primary placeholder:text-text-muted disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled focus-visible:border-border-focus aria-expanded:border-border-focus signal:transition-shadow signal:focus-visible:shadow-focus-ring text-input-value",
4489
+ "rounded-lg border bg-bg-field shadow-field text-left border-border-field outline-none transition-colors text-text-primary placeholder:text-text-muted disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled focus-visible:border-border-focus aria-expanded:border-border-focus signal:transition-shadow signal:focus-visible:shadow-focus-ring text-caption",
4490
+ "rounded-lg border bg-bg-field shadow-field text-left border-border-field outline-none transition-colors text-text-primary placeholder:text-text-muted disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled cursor-not-allowed bg-bg-disabled text-text-disabled focus-visible:border-border-focus aria-expanded:border-border-focus signal:transition-shadow signal:focus-visible:shadow-focus-ring text-input-value",
4491
+ "rounded-lg border bg-bg-field shadow-field text-left border-border-field outline-none transition-colors text-text-primary placeholder:text-text-muted disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled cursor-not-allowed bg-bg-disabled text-text-disabled focus-visible:border-border-focus aria-expanded:border-border-focus signal:transition-shadow signal:focus-visible:shadow-focus-ring text-caption",
4492
+ "rounded-lg border bg-bg-field shadow-field text-left border-border-field outline-none transition-colors text-text-primary placeholder:text-text-muted hover:border-border-strong focus-visible:hover:border-border-focus aria-expanded:hover:border-border-focus disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled focus-visible:border-border-focus aria-expanded:border-border-focus signal:transition-shadow signal:focus-visible:shadow-focus-ring text-input-value",
4493
+ "rounded-lg border bg-bg-field shadow-field text-left border-border-field outline-none transition-colors text-text-primary placeholder:text-text-muted hover:border-border-strong focus-visible:hover:border-border-focus aria-expanded:hover:border-border-focus disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled focus-visible:border-border-focus aria-expanded:border-border-focus signal:transition-shadow signal:focus-visible:shadow-focus-ring text-caption",
4494
+ "rounded-lg border bg-bg-field shadow-field text-left border-border-field outline-none transition-colors text-text-primary placeholder:text-text-muted hover:border-border-strong focus-visible:hover:border-border-focus aria-expanded:hover:border-border-focus disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled cursor-not-allowed bg-bg-disabled text-text-disabled focus-visible:border-border-focus aria-expanded:border-border-focus signal:transition-shadow signal:focus-visible:shadow-focus-ring text-input-value",
4495
+ "rounded-lg border bg-bg-field shadow-field text-left border-border-field outline-none transition-colors text-text-primary placeholder:text-text-muted hover:border-border-strong focus-visible:hover:border-border-focus aria-expanded:hover:border-border-focus disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled cursor-not-allowed bg-bg-disabled text-text-disabled focus-visible:border-border-focus aria-expanded:border-border-focus signal:transition-shadow signal:focus-visible:shadow-focus-ring text-caption",
4496
+ "rounded-lg border bg-bg-field shadow-field text-left"
4480
4497
  ]
4481
4498
  },
4482
4499
  {
@@ -4723,7 +4740,7 @@
4723
4740
  "docPage": "src/Textarea.mdx",
4724
4741
  "app": null,
4725
4742
  "looks": [
4726
- "bg-bg-inset border border-border-default hover:border-border-strong focus:border-border-focus rounded-lg text-input-value text-text-primary placeholder:text-text-muted resize-none outline-none transition-colors disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled signal:transition-shadow signal:focus:shadow-focus-ring"
4743
+ "bg-bg-field border border-border-field shadow-field hover:border-border-strong focus:border-border-focus rounded-lg text-input-value text-text-primary placeholder:text-text-muted resize-none outline-none transition-colors disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled signal:transition-shadow signal:focus:shadow-focus-ring"
4727
4744
  ]
4728
4745
  },
4729
4746
  {
@@ -4764,8 +4781,8 @@
4764
4781
  "docPage": "src/TextInput.mdx",
4765
4782
  "app": null,
4766
4783
  "looks": [
4767
- "bg-bg-inset border border-border-default hover:border-border-strong focus:border-border-focus rounded-lg text-input-value text-text-primary placeholder:text-text-muted outline-none transition-colors disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled signal:transition-shadow signal:focus:shadow-focus-ring",
4768
- "bg-bg-inset border border-border-default hover:border-border-strong focus:border-border-focus rounded-lg text-caption text-text-primary placeholder:text-text-muted outline-none transition-colors disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled signal:transition-shadow signal:focus:shadow-focus-ring"
4784
+ "bg-bg-field border border-border-field shadow-field hover:border-border-strong focus:border-border-focus rounded-lg text-input-value text-text-primary placeholder:text-text-muted outline-none transition-colors disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled signal:transition-shadow signal:focus:shadow-focus-ring",
4785
+ "bg-bg-field border border-border-field shadow-field hover:border-border-strong focus:border-border-focus rounded-lg text-caption text-text-primary placeholder:text-text-muted outline-none transition-colors disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled signal:transition-shadow signal:focus:shadow-focus-ring"
4769
4786
  ]
4770
4787
  },
4771
4788
  {
@@ -4847,11 +4864,11 @@
4847
4864
  "docPage": "src/Toast.mdx",
4848
4865
  "app": null,
4849
4866
  "looks": [
4850
- "bg-success-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md",
4851
- "bg-accent-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md",
4852
- "bg-bg-inset border border-border-subtle signal:border-border-default signal:shadow-md",
4853
- "bg-warning-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md",
4854
- "bg-error-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md"
4867
+ "bg-success-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md leaf:bg-bg-inset leaf:border leaf:border-border-default leaf:shadow-md",
4868
+ "bg-accent-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md leaf:bg-bg-inset leaf:border leaf:border-border-default leaf:shadow-md",
4869
+ "bg-bg-inset border border-border-subtle signal:border-border-default signal:shadow-md leaf:bg-bg-inset leaf:border leaf:border-border-default leaf:shadow-md",
4870
+ "bg-warning-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md leaf:bg-bg-inset leaf:border leaf:border-border-default leaf:shadow-md",
4871
+ "bg-error-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md leaf:bg-bg-inset leaf:border leaf:border-border-default leaf:shadow-md"
4855
4872
  ]
4856
4873
  },
4857
4874
  {
@@ -4896,11 +4913,11 @@
4896
4913
  "docPage": null,
4897
4914
  "app": null,
4898
4915
  "looks": [
4899
- "bg-success-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md",
4900
- "bg-accent-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md",
4901
- "bg-bg-inset border border-border-subtle signal:border-border-default signal:shadow-md",
4902
- "bg-warning-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md",
4903
- "bg-error-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md"
4916
+ "bg-success-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md leaf:bg-bg-inset leaf:border leaf:border-border-default leaf:shadow-md",
4917
+ "bg-accent-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md leaf:bg-bg-inset leaf:border leaf:border-border-default leaf:shadow-md",
4918
+ "bg-bg-inset border border-border-subtle signal:border-border-default signal:shadow-md leaf:bg-bg-inset leaf:border leaf:border-border-default leaf:shadow-md",
4919
+ "bg-warning-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md leaf:bg-bg-inset leaf:border leaf:border-border-default leaf:shadow-md",
4920
+ "bg-error-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md leaf:bg-bg-inset leaf:border leaf:border-border-default leaf:shadow-md"
4904
4921
  ]
4905
4922
  },
4906
4923
  {
@@ -5316,7 +5333,7 @@
5316
5333
  "docPage": "src/Tooltip.mdx",
5317
5334
  "app": null,
5318
5335
  "looks": [
5319
- "rounded-lg border border-border-default bg-bg-elevated shadow-lg"
5336
+ "rounded-lg border border-border-default bg-bg-elevated shadow-lg border-border-tooltip bg-bg-tooltip"
5320
5337
  ]
5321
5338
  },
5322
5339
  {
@@ -66,9 +66,7 @@ const rail = (
66
66
  export const Solid: Story = {
67
67
  render: (args) => (
68
68
  <AppShell {...args} logo="Estiva" search={<SearchInput shortcut="Ctrl+K" className="w-[290px]" />} identity={identity} nav={sidebar}>
69
- <div className="flex h-full items-center justify-center">
70
- <EmptyState message="Nothing here yet." />
71
- </div>
69
+ <EmptyState message="Nothing here yet." />
72
70
  </AppShell>
73
71
  ),
74
72
  }
@@ -92,9 +90,7 @@ export const SolidScrolls: Story = {
92
90
  export const SolidWithBanner: Story = {
93
91
  render: (args) => (
94
92
  <AppShell {...args} logo="Estiva" identity={identity} nav={sidebar} banner={<Banner tone="ok">Public key copied.</Banner>}>
95
- <div className="flex h-full items-center justify-center">
96
- <EmptyState message="Nothing here yet." />
97
- </div>
93
+ <EmptyState message="Nothing here yet." />
98
94
  </AppShell>
99
95
  ),
100
96
  }
@@ -111,9 +107,7 @@ export const Floating: Story = {
111
107
  identity={identity}
112
108
  nav={rail}
113
109
  >
114
- <div className="flex h-full items-center justify-center">
115
- <EmptyState message="Nothing here yet." />
116
- </div>
110
+ <EmptyState message="Nothing here yet." />
117
111
  </AppShell>
118
112
  ),
119
113
  }
@@ -130,9 +124,7 @@ export const FloatingWithBanner: Story = {
130
124
  nav={rail}
131
125
  banner={<Banner tone="info">A new version is available. Reload when convenient.</Banner>}
132
126
  >
133
- <div className="flex h-full items-center justify-center">
134
- <EmptyState message="Nothing here yet." />
135
- </div>
127
+ <EmptyState message="Nothing here yet." />
136
128
  </AppShell>
137
129
  ),
138
130
  }
@@ -390,7 +390,6 @@ export function AttachmentCard({
390
390
  {/* On Base UI's Button, as InputChip's ✕ is (Katerina, 2026-09-14): IconButton is a 24px square
391
391
  that fills on hover, and this is Peek's 20px round badge on the card's corner. */}
392
392
  {onRemove && (
393
- // @estiva-escape(no-copied-look): N4 (Katerina, 24 September: record only): the small round button Lightbox's close also draws; a part if a third appears
394
393
  <BaseButton
395
394
  type="button"
396
395
  aria-label={`Remove ${name}`}
@@ -175,7 +175,7 @@ type Key = [key: string, word: string]
175
175
  function Footer({ keys }: { keys: Key[] }) {
176
176
  const { where } = usePalette('The footer')
177
177
  return (
178
- <div className="flex h-9 shrink-0 items-center gap-4 border-t border-border-subtle px-5 text-caption text-text-secondary signal:font-mono signal:text-small signal:text-text-muted">
178
+ <div className="flex h-9 shrink-0 items-center gap-4 border-t border-border-subtle px-5 text-caption text-text-secondary signal:font-mono signal:text-small signal:text-text-muted leaf:font-mono leaf:text-small">
179
179
  <span className="min-w-0 flex-1 truncate">{where}</span>
180
180
  {keys.map(([key, word]) => (
181
181
  <span key={key} className="flex shrink-0 items-center gap-1.5">
@@ -16,7 +16,7 @@ page keeps working.
16
16
  - Around each part of the page that can break on its own: a column, a panel,
17
17
  a conversation — and once around the whole app.
18
18
  - `label` says what it holds, in the message and the console line: "Something
19
- went wrong in the {label}."
19
+ went wrong in the \{label\}."
20
20
  - `fallbackClassName` is the room the message fills where no flex column
21
21
  holds it: the whole screen, for the app's own.
22
22
  - `frame` draws the message inside a frame of yours, in the same place.
@@ -48,7 +48,7 @@ import { ErrorBoundary } from '@estiva-app/ui'
48
48
  is given and centres itself in it.
49
49
  - Try again draws the part again. If what broke is still broken, the message
50
50
  comes back.
51
- - A crash is written to the console: "{label} crashed:", the error, and where
51
+ - A crash is written to the console: "\{label\} crashed:", the error, and where
52
52
  it happened.
53
53
 
54
54
  ## What it owns
package/src/Kbd.tsx CHANGED
@@ -24,15 +24,24 @@ import { cn } from './cn'
24
24
  export interface KbdProps {
25
25
  children: ReactNode
26
26
  className?: string
27
+ /**
28
+ * `tooltip` draws the key in the tooltip's own key colours, for a theme whose
29
+ * tooltip is darker than its page (Katerina, 27 September). `Tooltip` passes it
30
+ * for its shortcut; nothing else needs to.
31
+ */
32
+ tone?: 'default' | 'tooltip'
27
33
  }
28
34
 
29
- export function Kbd({ children, className }: KbdProps) {
35
+ export function Kbd({ children, className, tone = 'default' }: KbdProps) {
30
36
  return (
31
37
  <kbd
32
38
  className={cn(
33
39
  'inline-flex shrink-0 items-center justify-center whitespace-nowrap rounded-sm border border-border-strong bg-bg-inset px-1 py-px font-sans text-caption text-text-secondary',
34
40
  'signal:border-b-2 signal:pt-[2px] signal:pb-px signal:bg-bg-wash signal:font-mono signal:text-small',
35
41
  'ship:border-b-2 ship:pt-[2px] ship:pb-px ship:bg-bg-wash ship:font-mono ship:text-small',
42
+ // Leaf sets its keys in mono, as Signal does (Katerina, 28 September): the type, not the key's shape.
43
+ 'leaf:font-mono leaf:text-small',
44
+ tone === 'tooltip' && 'border-border-tooltip-key bg-bg-tooltip-key text-text-tooltip-key signal:bg-bg-tooltip-key ship:bg-bg-tooltip-key',
36
45
  className,
37
46
  )}
38
47
  >
package/src/Lightbox.tsx CHANGED
@@ -74,10 +74,12 @@ export function Lightbox({ src, alt, onClose, className }: LightboxProps) {
74
74
  onClick={(e) => e.stopPropagation()}
75
75
  className={cn('max-w-full max-h-full object-contain rounded-lg', className)}
76
76
  />
77
- {/* @estiva-escape(no-copied-look): N4 (Katerina, 24 September: record only): the small round button AttachmentCard's remove also draws; a part if a third appears */}
77
+ {/* On the dark scrim in every theme, so it wears the tooltip's dark colours (Katerina,
78
+ 28 September: Leaf's white circle stood out); in the other themes they are the
79
+ elevated ground, the strong line and the secondary text it always had. */}
78
80
  <Dialog.Close
79
81
  aria-label="Close"
80
- className="absolute top-4 right-4 size-8 rounded-full bg-bg-elevated border border-border-strong flex items-center justify-center text-text-secondary hover:text-text-primary"
82
+ className="absolute top-4 right-4 size-8 rounded-full bg-bg-tooltip border border-border-tooltip-key flex items-center justify-center text-text-tooltip-key hover:text-text-tooltip"
81
83
  >
82
84
  <IconX size={16} stroke={1.5} />
83
85
  </Dialog.Close>
package/src/Menu.tsx CHANGED
@@ -552,7 +552,7 @@ export function EnterHint({ target }: { target?: string }) {
552
552
  <span className="flex shrink-0 items-center gap-1.5 text-text-muted">
553
553
  <Kbd>↩ Enter</Kbd>
554
554
  {target && (
555
- <span className="text-menu signal:font-mono signal:text-small signal:tracking-wider">{target}</span>
555
+ <span className="text-menu signal:font-mono signal:text-small signal:tracking-wider leaf:font-mono leaf:text-small leaf:tracking-wider">{target}</span>
556
556
  )}
557
557
  </span>
558
558
  )
@@ -48,15 +48,10 @@ export const WithAShortcut: Story = {
48
48
  args: { label: 'Copy link', leading: <IconCopy size={16} stroke={1.5} className="text-text-secondary" />, shortcut: 'Ctrl+C' },
49
49
  }
50
50
 
51
- /** The row opens another menu. */
52
- export const Submenu: Story = {
53
- args: { label: 'Move to…', submenu: true },
54
- }
55
-
56
51
  /**
57
- * The submenu, working. The story above draws the row at rest — the chevron is
58
- * the whole affordance — but a row that opens another menu can only be tried
59
- * inside a real one, so here is one.
52
+ * A row that opens another menu, working: such a row can only be tried inside
53
+ * a real menu, so this is one (Katerina, 28 September: one story, the working
54
+ * one; the chevron is the row's whole affordance).
60
55
  *
61
56
  * Hover **Move to…**, or arrow onto it and press →. The panel opens beside the
62
57
  * row, flips to the other side at a screen edge, and stays open while you
@@ -66,7 +61,7 @@ export const Submenu: Story = {
66
61
  * into it, and ← closes it and puts the highlight back on the row. Measured in
67
62
  * that order — → then ← alone does nothing, because focus has not entered yet.
68
63
  */
69
- export const SubmenuLive: Story = {
64
+ export const Submenu: Story = {
70
65
  parameters: { controls: { disable: true } },
71
66
  decorators: [(Story) => <Story />],
72
67
  render: () => (
@@ -61,7 +61,7 @@ export const WithAFooter: Story = {
61
61
  footer={
62
62
  // A stand-in for whatever the footer holds: the part only keeps it in place.
63
63
  <div className="border-t border-border-subtle p-3">
64
- <div className="flex h-10 items-center justify-center rounded-md bg-bg-elevated text-caption text-text-muted">Footer</div>
64
+ <div className="flex h-10 items-center justify-center rounded-md bg-bg-elevated text-caption text-text-secondary">Footer</div>
65
65
  </div>
66
66
  }
67
67
  >
@@ -190,6 +190,9 @@ export const FromASelection: Story = {
190
190
  side="top"
191
191
  ariaLabel="Formatting"
192
192
  className="w-auto min-w-0 p-1"
193
+ /* No padding of the popup's own: the panel's 4px is the Toolbar's, and
194
+ the default 8px on top of it drew the panel 16px taller (Katerina, 28 September). */
195
+ contentClassName="p-0"
193
196
  >
194
197
  <Toolbar aria-label="Formatting" surface={false}>
195
198
  <ToolbarButton aria-label="Bold" tooltip="Bold">
@@ -39,6 +39,8 @@ export function SectionLabel({
39
39
  <span
40
40
  className={cn(
41
41
  'text-h5 leading-3 signal:font-mono signal:text-small signal:uppercase signal:tracking-widest',
42
+ // Leaf's section labels are Signal's type (Katerina, 28 September).
43
+ 'leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest',
42
44
  tone === 'secondary' ? 'text-text-secondary' : tone === 'muted' ? 'text-text-muted' : 'text-text-primary',
43
45
  truncate && 'min-w-0 truncate',
44
46
  className,
package/src/Select.tsx CHANGED
@@ -88,8 +88,8 @@ export function Select({ value, onChange, options, size = 'default', ariaLabel,
88
88
  * `truncate` from ever engaging; in a block container max-w-full is
89
89
  * the cap. Full-width callers are unaffected.
90
90
  */
91
- 'flex w-full min-w-0 max-w-full items-center justify-between gap-2 rounded-lg border bg-bg-inset text-left',
92
- 'border-border-default outline-none transition-colors',
91
+ 'flex w-full min-w-0 max-w-full items-center justify-between gap-2 rounded-lg border bg-bg-field shadow-field text-left',
92
+ 'border-border-field outline-none transition-colors',
93
93
  FIELD_TEXT_CLASSES,
94
94
  // The focused border survives a hover: hover alone strengthens the
95
95
  // hairline, but hover while focused must not grey the focus colour —
@@ -40,6 +40,6 @@ describe('TextInput', () => {
40
40
  render(<TextInput aria-label="Title" />)
41
41
  const classes = screen.getByRole('textbox').className.split(' ')
42
42
  // Both survive cn(); Tailwind emits focus after hover, so the order in CSS decides, not here.
43
- for (const c of ['border-border-default', 'hover:border-border-strong', 'focus:border-border-focus']) expect(classes).toContain(c)
43
+ for (const c of ['border-border-field', 'hover:border-border-strong', 'focus:border-border-focus']) expect(classes).toContain(c)
44
44
  })
45
45
  })
package/src/Toast.tsx CHANGED
@@ -44,13 +44,14 @@ export interface ToastProps {
44
44
  }
45
45
 
46
46
  // Signal: every toast is the same dark overlay pill (v3) — the type lives in
47
- // the icon color + glow, not the surface.
47
+ // the icon color + glow, not the surface. Leaf draws them the same way, on its
48
+ // own greys (Katerina, 28 September).
48
49
  const SURFACE_STYLES: Record<ToastType, string> = {
49
- success: 'bg-success-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md',
50
- brand: 'bg-accent-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md',
51
- neutral: 'bg-bg-inset border border-border-subtle signal:border-border-default signal:shadow-md',
52
- warning: 'bg-warning-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md',
53
- error: 'bg-error-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md',
50
+ success: 'bg-success-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md leaf:bg-bg-inset leaf:border leaf:border-border-default leaf:shadow-md',
51
+ brand: 'bg-accent-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md leaf:bg-bg-inset leaf:border leaf:border-border-default leaf:shadow-md',
52
+ neutral: 'bg-bg-inset border border-border-subtle signal:border-border-default signal:shadow-md leaf:bg-bg-inset leaf:border leaf:border-border-default leaf:shadow-md',
53
+ warning: 'bg-warning-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md leaf:bg-bg-inset leaf:border leaf:border-border-default leaf:shadow-md',
54
+ error: 'bg-error-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md leaf:bg-bg-inset leaf:border leaf:border-border-default leaf:shadow-md',
54
55
  }
55
56
 
56
57
  /**
@@ -3,7 +3,7 @@ import { IconArchive, IconPin, IconTrash } from '@tabler/icons-react'
3
3
  import { Button } from './Button'
4
4
  import { IconButton } from './IconButton'
5
5
  import { Tooltip, WithTooltip } from './Tooltip'
6
- import { Card } from './Card'
6
+ import { Toolbar, ToolbarButton } from './Toolbar'
7
7
 
8
8
  const meta = {
9
9
  title: 'Primitives/Tooltip',
@@ -100,16 +100,16 @@ export const OnADisabledControl: Story = {
100
100
  export const InAToolbar: Story = {
101
101
  parameters: { controls: { disable: true } },
102
102
  render: () => (
103
- <Card fill="elevated" className="flex items-center gap-1 p-1">
104
- <IconButton aria-label="Pin" tooltip="Pin">
103
+ <Toolbar aria-label="Actions">
104
+ <ToolbarButton aria-label="Pin" tooltip="Pin">
105
105
  <IconPin size={16} stroke={1.5} />
106
- </IconButton>
107
- <IconButton aria-label="Archive" tooltip="Archive">
106
+ </ToolbarButton>
107
+ <ToolbarButton aria-label="Archive" tooltip="Archive">
108
108
  <IconArchive size={16} stroke={1.5} />
109
- </IconButton>
110
- <IconButton aria-label="Delete" tooltip="Delete" tooltipShortcut="Del">
109
+ </ToolbarButton>
110
+ <ToolbarButton aria-label="Delete" tooltip="Delete" tooltipShortcut="Del">
111
111
  <IconTrash size={16} stroke={1.5} />
112
- </IconButton>
113
- </Card>
112
+ </ToolbarButton>
113
+ </Toolbar>
114
114
  ),
115
115
  }
@@ -39,7 +39,7 @@ describe('Tooltip, the surface', () => {
39
39
  render(<Tooltip label="Bold" className="w-40" />)
40
40
  const tip = screen.getByRole('tooltip')
41
41
  expect(tip.className).toContain('w-40')
42
- expect(tip.className).toContain('bg-bg-elevated')
42
+ expect(tip.className).toContain('bg-bg-tooltip')
43
43
  })
44
44
  })
45
45
 
package/src/Tooltip.tsx CHANGED
@@ -73,9 +73,9 @@ export interface TooltipProps extends Omit<ComponentPropsWithRef<'div'>, 'childr
73
73
  */
74
74
  export function Tooltip({ label, shortcut, className, ...props }: TooltipProps) {
75
75
  return (
76
- <div role="tooltip" className={cn(FLOATING_SURFACE_CLASSES, 'min-h-[30px] max-w-80 flex items-center justify-center gap-1.5 px-2 py-1', className)} {...props}>
77
- <span className="min-w-0 text-caption text-text-primary break-words">{label}</span>
78
- {shortcut && <Kbd>{shortcut}</Kbd>}
76
+ <div role="tooltip" className={cn(FLOATING_SURFACE_CLASSES, 'border-border-tooltip bg-bg-tooltip', 'min-h-[30px] max-w-80 flex items-center justify-center gap-1.5 px-2 py-1', className)} {...props}>
77
+ <span className="min-w-0 text-caption text-text-tooltip break-words">{label}</span>
78
+ {shortcut && <Kbd tone="tooltip">{shortcut}</Kbd>}
79
79
  </div>
80
80
  )
81
81
  }
package/src/cn.ts CHANGED
@@ -29,7 +29,7 @@ const FONT_SIZE_TOKENS = [
29
29
  * `shadow-glow-warning` beside `shadow-md` kept both (measured 2026-09-06).
30
30
  * `cn.test.ts` pins these to the preset too.
31
31
  */
32
- const BOX_SHADOW_TOKENS = ['sm', 'md', 'lg', 'focus-ring', 'glow-warning', 'glow-success', 'glow-accent', 'highlight-inset']
32
+ const BOX_SHADOW_TOKENS = ['sm', 'md', 'lg', 'focus-ring', 'field', 'glow-warning', 'glow-success', 'glow-accent', 'highlight-inset']
33
33
  const DROP_SHADOW_TOKENS = ['glow-success', 'glow-accent', 'glow-warning']
34
34
 
35
35
  const twMerge = extendTailwindMerge({
@@ -28,6 +28,7 @@ import { noRawElement } from './no-raw-element'
28
28
  import { noRebuiltBehaviour } from './no-rebuilt-behaviour'
29
29
  import { noRestyledPart } from './no-restyled-part'
30
30
  import { rawElementOutsideAWrapper } from './raw-element-outside-a-wrapper'
31
+ import { APP_RULE_IDS, PLUGIN_KEY, type AppRuleName } from './rule-ids'
31
32
 
32
33
  export { ESCAPE_MARKER, MIN_REASON, SETTINGS_KEY, isEscaped, type EstivaSettings } from './escape'
33
34
  export { OWNED_BEHAVIOURS, type OwnedBehaviour } from './no-rebuilt-behaviour'
@@ -37,14 +38,15 @@ export { MIN_COVER, MIN_SHARED } from './looks-of'
37
38
 
38
39
  const { version } = createRequire(import.meta.url)('../../package.json') as { version: string }
39
40
 
40
- /** The name the configs register the plugin under, so every rule id is `estiva/<rule>`. */
41
- export const PLUGIN_KEY = 'estiva'
41
+ export { APP_RULE_IDS, PLUGIN_KEY }
42
42
 
43
43
  /**
44
44
  * The rules an **app** runs: they say an app must not build what the package
45
45
  * already has — a raw control (UIG-7), or a behaviour one of its parts owns
46
46
  * (UIG-8) — nor restyle a part it uses (UIG-9). `recommended` and `strict`
47
- * carry these and only these.
47
+ * carry these and only these. Their names live in `rule-ids.ts`, which
48
+ * `create-estiva-app` reads without loading a rule; `satisfies` fails the
49
+ * typecheck when a rule is missing from either.
48
50
  */
49
51
  const appRules = {
50
52
  'no-raw-element': noRawElement,
@@ -54,7 +56,7 @@ const appRules = {
54
56
  'no-handmade-empty-state': noHandmadeEmptyState,
55
57
  'no-native-title': noNativeTitle,
56
58
  'no-copied-look': noCopiedLook,
57
- }
59
+ } satisfies Record<AppRuleName, unknown>
58
60
 
59
61
  /**
60
62
  * The rules the **package itself** runs, pointed inward (UIG-5): don't bury a
@@ -94,8 +96,6 @@ const plugin = {
94
96
  configs: {} as { recommended: Linter.Config; strict: Linter.Config; package: Linter.Config },
95
97
  } satisfies ESLint.Plugin
96
98
 
97
- /** The ids `recommended` and `strict` carry — what an app's gate runs and counts. */
98
- export const APP_RULE_IDS = Object.keys(appRules).map((name) => `${PLUGIN_KEY}/${name}`)
99
99
  /** The ids `package` carries — what this package's own gate runs and counts (UIG-5). */
100
100
  export const PACKAGE_RULE_IDS = Object.keys(packageRules).map((name) => `${PLUGIN_KEY}/${name}`)
101
101
 
@@ -118,6 +118,7 @@ export const PART_LOOK_PROPS: Record<string, string[]> = {
118
118
  FieldLine: ['tone'],
119
119
  IconButton: ['variant', 'pressed', 'glow'],
120
120
  InputChip: ['truncate'],
121
+ Kbd: ['tone'],
121
122
  Link: ['variant', 'truncate'],
122
123
  MenuItem: ['size', 'selected', 'destructive'],
123
124
  MenuSub: ['selected'],