@motion-proto/live-tokens 0.82.0 → 0.84.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 (91) hide show
  1. package/.claude/skills/live-tokens-create-component/SKILL.md +16 -4
  2. package/.claude/skills/live-tokens-create-page/SKILL.md +19 -17
  3. package/.claude/skills/live-tokens-create-page/references/interaction-sources.md +3 -3
  4. package/.claude/skills/live-tokens-pick-component/SKILL.md +10 -83
  5. package/CHANGELOG.md +84 -0
  6. package/README.md +1 -1
  7. package/bin/cli.mjs +21 -8
  8. package/bin/lib/catalogue.mjs +140 -41
  9. package/bin/rules/componentStructure.mjs +12 -4
  10. package/dist-plugin/{chunk-REBHE3ZM.js → chunk-FD4FVDOC.js} +59 -9
  11. package/dist-plugin/{chunk-D4WRIKEZ.js → chunk-M6BWSS6C.js} +1 -1
  12. package/dist-plugin/{chunk-RFVYPNRO.js → chunk-V2OVSC5Z.js} +22 -1
  13. package/dist-plugin/index.cjs +85 -14
  14. package/dist-plugin/index.js +3 -3
  15. package/dist-plugin/migrateData/index.cjs +59 -9
  16. package/dist-plugin/migrateData/index.js +2 -2
  17. package/dist-plugin/setColors/index.cjs +59 -9
  18. package/dist-plugin/setColors/index.js +1 -1
  19. package/dist-plugin/setGeometry/index.cjs +63 -11
  20. package/dist-plugin/setGeometry/index.d.cts +1 -1
  21. package/dist-plugin/setGeometry/index.d.ts +1 -1
  22. package/dist-plugin/setGeometry/index.js +5 -3
  23. package/dist-plugin/tokensCssMigrations/index.cjs +22 -1
  24. package/dist-plugin/tokensCssMigrations/index.js +1 -1
  25. package/package.json +3 -1
  26. package/src/editor/component-editor/DialogEditor.svelte +2 -1
  27. package/src/editor/component-editor/ImageLightboxEditor.svelte +2 -1
  28. package/src/editor/component-editor/scaffolding/TokenLayout.svelte +4 -1
  29. package/src/editor/component-editor/scaffolding/types.ts +13 -8
  30. package/src/editor/core/components/aliasKinds.ts +5 -1
  31. package/src/editor/core/sketch/sketchLayer.ts +1 -1
  32. package/src/editor/core/themes/migrations/2026-09-20-imagelightbox-scrim.ts +28 -0
  33. package/src/editor/core/themes/migrations/2026-09-20-scrim-color-and-opacity.ts +45 -0
  34. package/src/editor/core/themes/migrations/index.ts +4 -0
  35. package/src/editor/docs/content/creating-components.md +3 -0
  36. package/src/editor/docs/content.generated.ts +1 -1
  37. package/src/editor/skill-atlas/SkillAtlas.svelte +65 -38
  38. package/src/editor/skill-atlas/SkillValue.svelte +202 -0
  39. package/src/editor/skill-atlas/evalResults.ts +46 -0
  40. package/src/editor/skill-atlas/skillSources.generated.ts +4 -4
  41. package/src/editor/skill-atlas/trees/create-component.ts +18 -18
  42. package/src/editor/skill-atlas/trees/create-page.ts +28 -32
  43. package/src/editor/skill-atlas/trees/pick-component.ts +66 -196
  44. package/src/editor/ui/UIVariantSelector.svelte +1 -1
  45. package/src/editor/ui/variantScales.ts +11 -0
  46. package/src/live-tokens/data/themes/autumn.json +6 -4
  47. package/src/live-tokens/data/themes/halloween.json +6 -4
  48. package/src/live-tokens/data/themes/midnight-study.json +6 -4
  49. package/src/live-tokens/data/themes/ocean.json +6 -4
  50. package/src/live-tokens/data/themes/royal-velvet.json +6 -4
  51. package/src/live-tokens/data/themes/sketchy.json +6 -4
  52. package/src/live-tokens/data/themes/spring-meadow.json +6 -4
  53. package/src/live-tokens/data/themes/sunset.json +6 -4
  54. package/src/system/components/Badge.svelte +5 -2
  55. package/src/system/components/Button.svelte +10 -2
  56. package/src/system/components/Callout.svelte +5 -2
  57. package/src/system/components/Card.svelte +8 -3
  58. package/src/system/components/CodeSnippet.svelte +6 -2
  59. package/src/system/components/CollapsibleSection.svelte +6 -2
  60. package/src/system/components/CornerBadge.svelte +5 -2
  61. package/src/system/components/Dialog.svelte +12 -4
  62. package/src/system/components/IconButton.svelte +5 -2
  63. package/src/system/components/Image.svelte +5 -2
  64. package/src/system/components/ImageLightbox.svelte +44 -4
  65. package/src/system/components/InlineEditActions.svelte +5 -2
  66. package/src/system/components/Input.svelte +6 -2
  67. package/src/system/components/MenuSelect.svelte +9 -2
  68. package/src/system/components/Notification.svelte +5 -2
  69. package/src/system/components/Panel.svelte +5 -2
  70. package/src/system/components/ProgressBar.svelte +5 -2
  71. package/src/system/components/RadioButton.svelte +5 -2
  72. package/src/system/components/SectionDivider.svelte +5 -2
  73. package/src/system/components/SegmentedControl.svelte +5 -2
  74. package/src/system/components/SideNavigation.svelte +5 -2
  75. package/src/system/components/Slider.svelte +6 -2
  76. package/src/system/components/TabBar.svelte +5 -2
  77. package/src/system/components/Table.svelte +4 -2
  78. package/src/system/components/Toggle.svelte +5 -2
  79. package/src/system/components/Tooltip.svelte +5 -2
  80. package/src/system/styles/tokens.css +12 -4
  81. package/src/testing-js/{chunk-ZMZQZ33J.js → chunk-WQJ6QB6Y.js} +8 -4
  82. package/src/testing-js/chunk-WQJ6QB6Y.js.map +1 -0
  83. package/src/testing-js/{chunk-Q3YIAAG3.js → chunk-XXABJNUU.js} +2 -2
  84. package/src/testing-js/component-behavior.contract.js +1 -1
  85. package/src/testing-js/component-editor.contract.js +1 -1
  86. package/src/testing-js/component-render.contract.js +1 -1
  87. package/src/testing-js/index.js +2 -2
  88. package/src/testing-js/page-compliance.contract.js +1 -1
  89. package/src/testing-js/vitest.js +2 -2
  90. package/src/testing-js/chunk-ZMZQZ33J.js.map +0 -1
  91. /package/src/testing-js/{chunk-Q3YIAAG3.js.map → chunk-XXABJNUU.js.map} +0 -0
@@ -2995,7 +2995,8 @@
2995
2995
  "createdAt": "2026-05-10T20:05:24.719Z",
2996
2996
  "updatedAt": "2026-08-19T22:39:32.785Z",
2997
2997
  "aliases": {
2998
- "--dialog-scrim-surface": "--scrim-high",
2998
+ "--dialog-scrim-color": "--scrim-color",
2999
+ "--dialog-scrim-opacity": "--scrim-opacity-high",
2999
3000
  "--dialog-surface": "--surface-neutral-lowest",
3000
3001
  "--dialog-border": "--border-neutral-strong",
3001
3002
  "--dialog-border-width": "--border-width-2",
@@ -3139,7 +3140,8 @@
3139
3140
  "--imagelightbox-tile-border": "--color-transparent",
3140
3141
  "--imagelightbox-tile-border-width": "--border-width-0",
3141
3142
  "--imagelightbox-tile-shadow": "--shadow-md",
3142
- "--imagelightbox-overlay-surface": "color-mix(in srgb, var(--color-neutral-950) 76%, transparent)",
3143
+ "--imagelightbox-scrim-color": "color-mix(in srgb, var(--color-neutral-950) 76%, transparent)",
3144
+ "--imagelightbox-scrim-opacity": "1",
3143
3145
  "--imagelightbox-chrome-surface": "--surface-neutral-low",
3144
3146
  "--imagelightbox-chrome-border": "--border-brand",
3145
3147
  "--imagelightbox-chrome-border-width": "--border-width-1",
@@ -3993,5 +3995,5 @@
3993
3995
  }
3994
3996
  }
3995
3997
  },
3996
- "componentSchemaVersion": 35
3997
- }
3998
+ "componentSchemaVersion": 37
3999
+ }
@@ -3026,7 +3026,8 @@
3026
3026
  "createdAt": "2026-05-10T20:05:24.719Z",
3027
3027
  "updatedAt": "2026-08-19T22:39:32.785Z",
3028
3028
  "aliases": {
3029
- "--dialog-scrim-surface": "--scrim-high",
3029
+ "--dialog-scrim-color": "--scrim-color",
3030
+ "--dialog-scrim-opacity": "--scrim-opacity-high",
3030
3031
  "--dialog-surface": "--surface-neutral-lowest",
3031
3032
  "--dialog-border": "--border-neutral-strong",
3032
3033
  "--dialog-border-width": "--border-width-3",
@@ -3170,7 +3171,8 @@
3170
3171
  "--imagelightbox-tile-border": "--color-transparent",
3171
3172
  "--imagelightbox-tile-border-width": "--border-width-1",
3172
3173
  "--imagelightbox-tile-shadow": "--shadow-md",
3173
- "--imagelightbox-overlay-surface": "color-mix(in srgb, var(--color-neutral-950) 76%, transparent)",
3174
+ "--imagelightbox-scrim-color": "color-mix(in srgb, var(--color-neutral-950) 76%, transparent)",
3175
+ "--imagelightbox-scrim-opacity": "1",
3174
3176
  "--imagelightbox-chrome-surface": "--surface-neutral-low",
3175
3177
  "--imagelightbox-chrome-border": "--border-brand",
3176
3178
  "--imagelightbox-chrome-border-width": "--border-width-2",
@@ -4024,5 +4026,5 @@
4024
4026
  }
4025
4027
  }
4026
4028
  },
4027
- "componentSchemaVersion": 35
4028
- }
4029
+ "componentSchemaVersion": 37
4030
+ }
@@ -3009,7 +3009,8 @@
3009
3009
  "createdAt": "2026-05-10T20:05:24.719Z",
3010
3010
  "updatedAt": "2026-08-25T01:13:11.520Z",
3011
3011
  "aliases": {
3012
- "--dialog-scrim-surface": "--scrim-high",
3012
+ "--dialog-scrim-color": "--scrim-color",
3013
+ "--dialog-scrim-opacity": "--scrim-opacity-high",
3013
3014
  "--dialog-surface": "--surface-neutral-lowest",
3014
3015
  "--dialog-border": "--border-neutral-strong",
3015
3016
  "--dialog-border-width": "--border-width-2",
@@ -3153,7 +3154,8 @@
3153
3154
  "--imagelightbox-tile-border": "--color-transparent",
3154
3155
  "--imagelightbox-tile-border-width": "--border-width-1",
3155
3156
  "--imagelightbox-tile-shadow": "--shadow-md",
3156
- "--imagelightbox-overlay-surface": "color-mix(in srgb, var(--color-neutral-950) 76%, transparent)",
3157
+ "--imagelightbox-scrim-color": "color-mix(in srgb, var(--color-neutral-950) 76%, transparent)",
3158
+ "--imagelightbox-scrim-opacity": "1",
3157
3159
  "--imagelightbox-chrome-surface": "--surface-neutral-low",
3158
3160
  "--imagelightbox-chrome-border": "--border-brand",
3159
3161
  "--imagelightbox-chrome-border-width": "--border-width-1",
@@ -4007,5 +4009,5 @@
4007
4009
  }
4008
4010
  }
4009
4011
  },
4010
- "componentSchemaVersion": 35
4011
- }
4012
+ "componentSchemaVersion": 37
4013
+ }
@@ -2982,7 +2982,8 @@
2982
2982
  "createdAt": "2026-05-10T20:05:24.719Z",
2983
2983
  "updatedAt": "2026-08-19T22:39:32.785Z",
2984
2984
  "aliases": {
2985
- "--dialog-scrim-surface": "--scrim-high",
2985
+ "--dialog-scrim-color": "--scrim-color",
2986
+ "--dialog-scrim-opacity": "--scrim-opacity-high",
2986
2987
  "--dialog-surface": "--surface-neutral-lowest",
2987
2988
  "--dialog-border": "--border-neutral-strong",
2988
2989
  "--dialog-border-width": "--border-width-2",
@@ -3126,7 +3127,8 @@
3126
3127
  "--imagelightbox-tile-border": "--color-transparent",
3127
3128
  "--imagelightbox-tile-border-width": "--border-width-0",
3128
3129
  "--imagelightbox-tile-shadow": "--shadow-md",
3129
- "--imagelightbox-overlay-surface": "color-mix(in srgb, var(--color-neutral-950) 76%, transparent)",
3130
+ "--imagelightbox-scrim-color": "color-mix(in srgb, var(--color-neutral-950) 76%, transparent)",
3131
+ "--imagelightbox-scrim-opacity": "1",
3130
3132
  "--imagelightbox-chrome-surface": "--surface-neutral-low",
3131
3133
  "--imagelightbox-chrome-border": "--border-brand",
3132
3134
  "--imagelightbox-chrome-border-width": "--border-width-1",
@@ -3980,5 +3982,5 @@
3980
3982
  }
3981
3983
  }
3982
3984
  },
3983
- "componentSchemaVersion": 35
3984
- }
3985
+ "componentSchemaVersion": 37
3986
+ }
@@ -3034,7 +3034,8 @@
3034
3034
  "createdAt": "2026-05-10T20:05:24.719Z",
3035
3035
  "updatedAt": "2026-08-19T22:39:32.785Z",
3036
3036
  "aliases": {
3037
- "--dialog-scrim-surface": "--scrim-high",
3037
+ "--dialog-scrim-color": "--scrim-color",
3038
+ "--dialog-scrim-opacity": "--scrim-opacity-high",
3038
3039
  "--dialog-surface": "--surface-neutral-lowest",
3039
3040
  "--dialog-border": "--border-neutral-strong",
3040
3041
  "--dialog-border-width": "--border-width-2",
@@ -3178,7 +3179,8 @@
3178
3179
  "--imagelightbox-tile-border": "--color-transparent",
3179
3180
  "--imagelightbox-tile-border-width": "--border-width-0",
3180
3181
  "--imagelightbox-tile-shadow": "--shadow-md",
3181
- "--imagelightbox-overlay-surface": "color-mix(in srgb, var(--color-neutral-950) 76%, transparent)",
3182
+ "--imagelightbox-scrim-color": "color-mix(in srgb, var(--color-neutral-950) 76%, transparent)",
3183
+ "--imagelightbox-scrim-opacity": "1",
3182
3184
  "--imagelightbox-chrome-surface": "--surface-neutral-low",
3183
3185
  "--imagelightbox-chrome-border": "--border-brand",
3184
3186
  "--imagelightbox-chrome-border-width": "--border-width-1",
@@ -4032,5 +4034,5 @@
4032
4034
  }
4033
4035
  }
4034
4036
  },
4035
- "componentSchemaVersion": 35
4036
- }
4037
+ "componentSchemaVersion": 37
4038
+ }
@@ -3,8 +3,11 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'A short standing label on an element.',
6
- useFor: 'status the reader takes at a glance, such as "Beta", "New", or a version.',
7
- notFor: "a marker that sits on a parent's corner (CornerBadge); feedback after an action (Notification).",
6
+ whenToUse: 'status the reader takes at a glance, such as "Beta", "New", or a version.',
7
+ whenNotToUse: [
8
+ { when: "the marker sits on the thing it describes, in the parent's corner.", use: 'cornerbadge' },
9
+ { when: 'the message is feedback about something that just happened.', use: 'notification' },
10
+ ],
8
11
  props: {
9
12
  variant: '`brand`, `accent`, `special`, `alternate`, `canvas`, and `neutral` paint a color family; `success`, `warning`, `danger`, and `info` carry a status.',
10
13
  },
@@ -3,8 +3,16 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'A labelled action.',
6
- useFor: 'an action that needs a word to be unambiguous.',
7
- notFor: 'an icon-only action (IconButton); a link to another page.',
6
+ whenToUse: 'an action that needs a word to be unambiguous.',
7
+ whenNotToUse: [
8
+ { when: 'the glyph alone is plain, and space is short.', use: 'iconbutton' },
9
+ { when: 'the actions confirm or cancel an inline edit.', use: 'inlineeditactions' },
10
+ ],
11
+ constraints: [
12
+ 'One primary Button per page.',
13
+ 'A danger Button opens a Dialog to confirm before it runs.',
14
+ 'A link to another page is an anchor.',
15
+ ],
8
16
  props: {
9
17
  variant: 'one primary per page; secondary for the rest; outline for a tertiary action; danger for a destructive one.',
10
18
  },
@@ -3,8 +3,11 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'A standing message that sits inside a section.',
6
- useFor: 'something the reader must know about the content around it.',
7
- notFor: 'feedback after an action (Notification); a note that hangs off one element (Tooltip).',
6
+ whenToUse: 'something the reader must know about the content around it.',
7
+ whenNotToUse: [
8
+ { when: 'the message is feedback about something that just happened, and the reader can dismiss it.', use: 'notification' },
9
+ { when: 'the note hangs off one element, shown on hover or focus.', use: 'tooltip' },
10
+ ],
8
11
  props: {
9
12
  variant: '`info` notes, `success` confirms, `warning` cautions, `danger` flags harm.',
10
13
  },
@@ -3,9 +3,14 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'One item with a title.',
6
- useFor: 'an item on its own, or each item in a set: a product, a record, a plan the reader may act on.',
7
- notFor:
8
- "a section of the page's content (Panel); content the reader opens on demand (CollapsibleSection); a decision that blocks the page (Dialog).",
6
+ whenToUse: 'an item on its own, or each item in a set: a product, a record, a plan the reader may act on.',
7
+ whenNotToUse: [
8
+ { when: "the block is a stage, a list, a form, or copy that forms one section of the page's content.", use: 'panel' },
9
+ { when: 'the content is secondary, and most readers skip it until they open it.', use: 'collapsiblesection' },
10
+ { when: 'the page cannot continue until the reader decides.', use: 'dialog' },
11
+ { when: 'the reader scans and compares the records.', use: 'table' },
12
+ ],
13
+ constraints: ["The title prop is the Card's title, and the card's own tokens type it."],
9
14
  props: {
10
15
  variant: '`bare` drops the header for content that carries its own title.',
11
16
  },
@@ -3,8 +3,12 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'A command or value with a copy button.',
6
- useFor: 'text the reader is meant to run or paste somewhere else.',
7
- notFor: 'prose the reader only reads (a paragraph in its Card or Panel); a value the reader edits (Input).',
6
+ whenToUse: 'text the reader is meant to run or paste somewhere else.',
7
+ whenNotToUse: [
8
+ { when: 'the text is prose the reader only reads, inside its own titled block.', use: 'card' },
9
+ { when: 'the text is prose the reader only reads, inside a section of the page.', use: 'panel' },
10
+ { when: 'the reader edits the value.', use: 'input' },
11
+ ],
8
12
  } satisfies CatalogueEntry;
9
13
  </script>
10
14
 
@@ -3,8 +3,12 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'A section the reader opens and closes.',
6
- useFor: 'secondary content that most readers skip.',
7
- notFor: 'content every reader needs (Card); moving between pages (SideNavigation).',
6
+ whenToUse: 'secondary content that most readers skip.',
7
+ whenNotToUse: [
8
+ { when: 'every reader needs the content, so it stays open.', use: 'card' },
9
+ { when: 'the choice moves the reader to another page.', use: 'sidenavigation' },
10
+ ],
11
+ constraints: ["In a form, CollapsibleSection holds the secondary fields, and the form's actions stay outside it on the form's bottom edge."],
8
12
  props: {
9
13
  variant: '`chromeless` sits inside other content, `hairline` draws a line under the header, `container` frames the whole section.',
10
14
  },
@@ -3,8 +3,11 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'A badge that sits on a corner of its parent.',
6
- useFor: 'a count or status marker that sits on the thing it describes.',
7
- notFor: 'a label in the text flow (Badge); feedback after an action (Notification).',
6
+ whenToUse: 'a count or status marker that sits on the thing it describes.',
7
+ whenNotToUse: [
8
+ { when: 'the label sits in the text flow.', use: 'badge' },
9
+ { when: 'the message is feedback about something that just happened.', use: 'notification' },
10
+ ],
8
11
  props: {
9
12
  variant: '`brand`, `accent`, `special`, `alternate`, `canvas`, and `neutral` paint a color family; `success`, `warning`, `danger`, and `info` carry a status.',
10
13
  anchor: 'the corner the badge sits on; `bottom-right` unless named.',
@@ -3,8 +3,11 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'A modal window that blocks the page.',
6
- useFor: 'a decision the page cannot continue without, such as a destructive confirmation or a sign-in.',
7
- notFor: 'a routine form (Panel); a message that asks nothing of the reader (Notification).',
6
+ whenToUse: 'a decision the page cannot continue without, such as a destructive confirmation or a sign-in.',
7
+ whenNotToUse: [
8
+ { when: 'the form is routine, and the page can continue around it.', use: 'panel' },
9
+ { when: 'the message asks nothing of the reader.', use: 'notification' },
10
+ ],
8
11
  } satisfies CatalogueEntry;
9
12
  </script>
10
13
 
@@ -177,7 +180,8 @@
177
180
  <style>
178
181
  :global(:root) {
179
182
  /* Scrim */
180
- --dialog-scrim-surface: var(--scrim-high);
183
+ --dialog-scrim-color: var(--scrim-color);
184
+ --dialog-scrim-opacity: var(--scrim-opacity-high);
181
185
 
182
186
  /* Dialog frame */
183
187
  --dialog-surface: var(--surface-neutral-lowest);
@@ -224,7 +228,11 @@
224
228
  left: 0;
225
229
  width: 100%;
226
230
  height: 100%;
227
- background: var(--dialog-scrim-surface);
231
+ background: color-mix(
232
+ in srgb,
233
+ var(--dialog-scrim-color) calc(var(--dialog-scrim-opacity) * 100%),
234
+ transparent
235
+ );
228
236
  display: flex;
229
237
  justify-content: center;
230
238
  align-items: center;
@@ -3,8 +3,11 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'An action that an icon alone carries.',
6
- useFor: 'an action whose meaning is plain from the glyph, such as close, edit, or delete.',
7
- notFor: 'an action that needs a word (Button); the confirm-and-cancel pair after an inline edit (InlineEditActions).',
6
+ whenToUse: 'an action whose meaning is plain from the glyph, such as close, edit, or delete.',
7
+ whenNotToUse: [
8
+ { when: 'the action needs a word to be unambiguous.', use: 'button' },
9
+ { when: 'the pair confirms or cancels an inline edit.', use: 'inlineeditactions' },
10
+ ],
8
11
  props: {
9
12
  variant: 'one primary per page; secondary for the rest; outline for a tertiary action; danger for a destructive one.',
10
13
  },
@@ -3,8 +3,11 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'A framed picture in the page flow.',
6
- useFor: 'any picture the page shows.',
7
- notFor: 'a picture whose detail the reader must open (ImageLightbox); decoration that page CSS paints.',
6
+ whenToUse: 'any picture the page shows.',
7
+ whenNotToUse: [
8
+ { when: 'the detail is the point, and the reader opens it to see more.', use: 'imagelightbox' },
9
+ ],
10
+ constraints: ['Page CSS paints decoration.'],
8
11
  props: {
9
12
  variant:
10
13
  "`default` keeps the picture's own proportions, `banner` gives it a tall frame, `medium` and `compact` shorter ones.",
@@ -3,8 +3,10 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'A picture that opens to a full view on click.',
6
- useFor: 'a screenshot, artwork, or chart whose detail is the point, and for a gallery of them.',
7
- notFor: 'decoration or a picture the reader takes in at a glance (Image).',
6
+ whenToUse: 'a screenshot, artwork, or chart whose detail is the point, and for a gallery of them.',
7
+ whenNotToUse: [
8
+ { when: 'the reader takes the picture in at a glance, with no detail to open.', use: 'image' },
9
+ ],
8
10
  } satisfies CatalogueEntry;
9
11
  </script>
10
12
 
@@ -41,6 +43,29 @@
41
43
  a non-zero spread has no shadow under `content` — `drop-shadow()` has no
42
44
  spread slot. */
43
45
  shadow?: 'box' | 'content' | 'none';
46
+ /** A ground behind the image, for this instance, as any CSS fill. Overrides
47
+ `--imagelightbox-tile-surface`, which a theme sets for every tile at once
48
+ and which defaults to transparent. Art with transparency wants one:
49
+ without a ground the page shows through the art's own gaps while a `box`
50
+ shadow still casts from the rectangle around them. */
51
+ surface?: string | undefined;
52
+ /** A line around the tile, for this instance, as any CSS colour. Overrides
53
+ `--imagelightbox-tile-border`. It paints nothing on its own: the width
54
+ is a second token, so pass `borderWidth` with it wherever the theme
55
+ leaves that at zero. */
56
+ border?: string | undefined;
57
+ /** The width of that line, as any CSS length. Overrides
58
+ `--imagelightbox-tile-border-width`. */
59
+ borderWidth?: string | undefined;
60
+ /** The scrim's colour behind the open image, for this instance. Overrides
61
+ `--imagelightbox-scrim-color`. The modal portals to <body>, out of reach
62
+ of a wrapper around the tile, so a page that wants its own scrim has to
63
+ say so here. */
64
+ scrim?: string | undefined;
65
+ /** How much of that colour covers the page, 0 to 1. Overrides
66
+ `--imagelightbox-scrim-opacity`, and stands on its own: the colour it
67
+ mixes with is whatever the theme holds. */
68
+ scrimOpacity?: number | undefined;
44
69
  /** When true, shows a bottom toolbar (zoom in/out + percent) and a top-right close button, and enables wheel/drag zoom inside the open modal. When false, click anywhere closes. */
45
70
  extended?: boolean;
46
71
  /** Maximum zoom, as a multiple of the image's natural resolution: `1` = 100%
@@ -73,6 +98,11 @@
73
98
  maxWidth = undefined,
74
99
  fit = 'contain',
75
100
  shadow = 'box',
101
+ surface = undefined,
102
+ border = undefined,
103
+ borderWidth = undefined,
104
+ scrim = undefined,
105
+ scrimOpacity = undefined,
76
106
  extended = false,
77
107
  maxZoom = undefined,
78
108
  capNatural = false,
@@ -603,6 +633,9 @@
603
633
  class:shadow-box={shadow === 'box'}
604
634
  class:shadow-content={shadow === 'content'}
605
635
  style:--imagelightbox-tile-object-fit={fit}
636
+ style:--imagelightbox-tile-surface={surface}
637
+ style:--imagelightbox-tile-border={border}
638
+ style:--imagelightbox-tile-border-width={borderWidth}
606
639
  type="button"
607
640
  aria-label={cover?.alt ? `Expand image: ${cover.alt}` : 'Expand image'}
608
641
  aria-haspopup="dialog"
@@ -625,6 +658,8 @@
625
658
  <div
626
659
  bind:this={overlayEl}
627
660
  class="image-lightbox-overlay"
661
+ style:--imagelightbox-scrim-color={scrim}
662
+ style:--imagelightbox-scrim-opacity={scrimOpacity}
628
663
  class:active={open}
629
664
  aria-hidden="true"
630
665
  onclick={closeLightbox}
@@ -748,7 +783,8 @@
748
783
  --imagelightbox-tile-object-fit: contain;
749
784
 
750
785
  /* overlay */
751
- --imagelightbox-overlay-surface: color-mix(in srgb, var(--color-neutral-950) 76%, transparent);
786
+ --imagelightbox-scrim-color: var(--scrim-color);
787
+ --imagelightbox-scrim-opacity: var(--scrim-opacity-high);
752
788
 
753
789
  /* chrome (toolbar + close button) */
754
790
  --imagelightbox-chrome-surface: var(--surface-neutral-low);
@@ -874,7 +910,11 @@
874
910
  .image-lightbox-overlay {
875
911
  position: fixed;
876
912
  inset: 0;
877
- background: var(--imagelightbox-overlay-surface);
913
+ background: color-mix(
914
+ in srgb,
915
+ var(--imagelightbox-scrim-color) calc(var(--imagelightbox-scrim-opacity) * 100%),
916
+ transparent
917
+ );
878
918
  backdrop-filter: blur(var(--blur-md));
879
919
  z-index: var(--z-overlay);
880
920
  opacity: 0;
@@ -3,8 +3,11 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'The confirm-and-cancel pair that ends an inline edit.',
6
- useFor: 'the two actions that close an in-place rename or value edit.',
7
- notFor: 'the actions of a form or a dialog (Button); a lone action on a row (IconButton).',
6
+ whenToUse: 'the two actions that close an in-place rename or value edit.',
7
+ whenNotToUse: [
8
+ { when: 'the actions belong to a form or a Dialog.', use: 'button' },
9
+ { when: 'the row needs one lone action.', use: 'iconbutton' },
10
+ ],
8
11
  } satisfies CatalogueEntry;
9
12
  </script>
10
13
 
@@ -3,8 +3,12 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'A text field with its label, hint, and error line.',
6
- useFor: 'an answer the page cannot list, such as a name, an amount, or a search string.',
7
- notFor: 'a choice from a set the page can write down (MenuSelect); an on/off setting (Toggle).',
6
+ whenToUse: 'an answer the page cannot list, such as a name, an amount, or a search string.',
7
+ whenNotToUse: [
8
+ { when: 'the answer comes from a predefined list of options.', use: 'menuselect' },
9
+ { when: 'the setting is on or off, and takes effect at once.', use: 'toggle' },
10
+ { when: 'the position on a track carries the meaning of the number.', use: 'slider' },
11
+ ],
8
12
  props: {
9
13
  type: '`number` for an amount, `search` for a query, `password` for a secret; `text` otherwise.',
10
14
  },
@@ -3,8 +3,15 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'A list of options with one checked.',
6
- useFor: 'single selection from a set too long to sit in a row, dropped from a Button.',
7
- notFor: 'two to four inline alternatives (SegmentedControl); a form-style list read in full (RadioButton).',
6
+ whenToUse: 'single selection from a set too long to sit in a row, dropped from a Button.',
7
+ whenNotToUse: [
8
+ { when: 'the choice allows more than one value.' },
9
+ { when: 'the set is two to four options that fit inline.', use: 'segmentedcontrol' },
10
+ { when: 'the reader reads every option as text inside a larger form.', use: 'radiobutton' },
11
+ ],
12
+ constraints: [
13
+ 'The Button that opens it is the control on the page; MenuSelect renders its list open.',
14
+ ],
8
15
  props: {
9
16
  role: '`listbox` for a select; `menu` for a command menu.',
10
17
  },
@@ -3,8 +3,11 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'Feedback about something that just happened.',
6
- useFor: 'the outcome of an action or a system event, which the reader can dismiss.',
7
- notFor: 'standing content in a section (Callout); a decision that blocks the page (Dialog).',
6
+ whenToUse: 'the outcome of an action or a system event, which the reader can dismiss.',
7
+ whenNotToUse: [
8
+ { when: 'the reader must know it about the content around it, and it stays present.', use: 'callout' },
9
+ { when: 'the page cannot continue until the reader decides.', use: 'dialog' },
10
+ ],
8
11
  props: {
9
12
  variant: '`info` notes, `success` confirms, `warning` cautions, `danger` flags harm.',
10
13
  },
@@ -3,8 +3,11 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: "A frame around one section of the page's content.",
6
- useFor: 'a stage, a list, a form, or a block of copy that forms one section.',
7
- notFor: 'one item in a set (Card); a modal window (Dialog).',
6
+ whenToUse: 'a stage, a list, a form, or a block of copy that forms one section.',
7
+ whenNotToUse: [
8
+ { when: 'the block is one item, or each item in a set.', use: 'card' },
9
+ { when: 'the page cannot continue until the reader decides.', use: 'dialog' },
10
+ ],
8
11
  props: {
9
12
  minHeight: 'holds the height while the content changes.',
10
13
  },
@@ -3,8 +3,11 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'A read-out of progress against a labelled track.',
6
- useFor: 'reporting how far a task has run.',
7
- notFor: 'a number the reader sets (Slider); a standing status label (Badge).',
6
+ whenToUse: 'reporting how far a task has run.',
7
+ whenNotToUse: [
8
+ { when: 'the reader sets the number.', use: 'slider' },
9
+ { when: 'the label stands on its own, with no progress to report.', use: 'badge' },
10
+ ],
8
11
  } satisfies CatalogueEntry;
9
12
  </script>
10
13
 
@@ -3,8 +3,11 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'A form row that selects one option.',
6
- useFor: 'a choice the reader reviews as text before committing to a larger form.',
7
- notFor: 'an inline switch between views (SegmentedControl); a setting that takes effect at once (Toggle).',
6
+ whenToUse: 'a choice the reader reviews as text before committing to a larger form.',
7
+ whenNotToUse: [
8
+ { when: 'the choice switches between views of the same data, inline.', use: 'segmentedcontrol' },
9
+ { when: 'the setting takes effect the moment it flips.', use: 'toggle' },
10
+ ],
8
11
  } satisfies CatalogueEntry;
9
12
  </script>
10
13
 
@@ -3,8 +3,11 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'A titled break between the sections of a page.',
6
- useFor: 'naming the section that follows it.',
7
- notFor: 'moving between pages (SideNavigation); a title inside a container (Card).',
6
+ whenToUse: 'naming the section that follows it.',
7
+ whenNotToUse: [
8
+ { when: 'the titles are links that move the reader between pages.', use: 'sidenavigation' },
9
+ { when: 'the title belongs to one item inside a container.', use: 'card' },
10
+ ],
8
11
  props: {
9
12
  variant: '`sm` titles a section, `md` titles a page, and `lg` is the largest size.',
10
13
  eyebrow: 'an opt-in label above the title.',
@@ -3,8 +3,11 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'An inline switch between named alternatives.',
6
- useFor: 'two to four short labels that pick one view of the same data.',
7
- notFor: "swapping the page's content area (TabBar); labels long enough to wrap (RadioButton).",
6
+ whenToUse: 'two to four short labels that pick one view of the same data.',
7
+ whenNotToUse: [
8
+ { when: "the choice swaps the page's content area below it.", use: 'tabbar' },
9
+ { when: 'a label would wrap.', use: 'radiobutton' },
10
+ ],
8
11
  } satisfies CatalogueEntry;
9
12
  </script>
10
13
 
@@ -3,8 +3,11 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'The rail that moves between the pages of a site.',
6
- useFor: 'navigation that follows the current path.',
7
- notFor: 'switching views inside one page (TabBar); a menu of actions (MenuSelect).',
6
+ whenToUse: 'navigation that follows the current path.',
7
+ whenNotToUse: [
8
+ { when: 'the switch stays inside one page, with no URL change.', use: 'tabbar' },
9
+ { when: 'the list holds actions.', use: 'menuselect' },
10
+ ],
8
11
  } satisfies CatalogueEntry;
9
12
  </script>
10
13
 
@@ -3,8 +3,12 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'A number the reader picks by position on a track.',
6
- useFor: 'a volume, a price range, or a percentage, where the position carries the meaning.',
7
- notFor: 'an exact number the reader would rather type (Input); an on/off setting (Toggle).',
6
+ whenToUse: 'a volume, a price range, or a percentage, where the position carries the meaning.',
7
+ whenNotToUse: [
8
+ { when: 'the reader knows the exact number and would rather type it.', use: 'input' },
9
+ { when: 'the setting is on or off, and takes effect at once.', use: 'toggle' },
10
+ { when: 'the number is a read-out of progress.', use: 'progressbar' },
11
+ ],
8
12
  props: {
9
13
  variant: '`single` picks one number; `range` picks a low and a high.',
10
14
  },
@@ -3,8 +3,11 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'Tabs that swap the content below them.',
6
- useFor: 'two to seven views the reader moves between inside one page.',
7
- notFor: 'one switch among others in a row (SegmentedControl); navigation that changes the URL (SideNavigation).',
6
+ whenToUse: 'two to seven views the reader moves between inside one page.',
7
+ whenNotToUse: [
8
+ { when: 'the switch sits among other controls in a row.', use: 'segmentedcontrol' },
9
+ { when: 'the choice changes the URL.', use: 'sidenavigation' },
10
+ ],
8
11
  } satisfies CatalogueEntry;
9
12
  </script>
10
13
 
@@ -3,8 +3,10 @@
3
3
 
4
4
  export const catalogue = {
5
5
  description: 'A themed frame around the rows and cells the page writes.',
6
- useFor: 'records the reader scans and compares.',
7
- notFor: 'a set of things the reader acts on (Card).',
6
+ whenToUse: 'records the reader scans and compares.',
7
+ whenNotToUse: [
8
+ { when: 'the reader acts on each item.', use: 'card' },
9
+ ],
8
10
  } satisfies CatalogueEntry;
9
11
  </script>
10
12