@threadlabs/looma 0.37.0 → 0.37.1

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 (162) hide show
  1. package/components/shared/icons.js +3 -0
  2. package/components/ui-button/ui-button.html +3 -12
  3. package/components/ui-card/ui-card.html +5 -5
  4. package/components/ui-checkbox/ui-checkbox.html +0 -6
  5. package/components/ui-color-picker/ui-color-picker.html +0 -9
  6. package/components/ui-combobox/ui-combobox.html +1 -13
  7. package/components/ui-cycle-button/ui-cycle-button.html +41 -0
  8. package/components/ui-cycle-button/ui-cycle-button.js +19 -0
  9. package/components/ui-dialog/ui-dialog.html +1 -1
  10. package/components/ui-icon/ui-icon.html +3 -0
  11. package/components/ui-icon-button/ui-icon-button.html +10 -14
  12. package/components/ui-input/ui-input.html +1 -16
  13. package/components/ui-input-group/ui-input-group.html +0 -10
  14. package/components/ui-menu/ui-menu.html +1 -1
  15. package/components/ui-popover/ui-popover.html +1 -1
  16. package/components/ui-radio/ui-radio.html +0 -6
  17. package/components/ui-select/ui-select.html +2 -8
  18. package/components/ui-switch/ui-switch.html +0 -6
  19. package/components/ui-tooltip/ui-tooltip.html +1 -1
  20. package/dist/index.js +47 -46
  21. package/docs/README.md +45 -6
  22. package/docs/card.html +2 -1
  23. package/docs/catalog.json +212 -37
  24. package/docs/customizer-render.mjs +7 -6
  25. package/docs/customizer.js +13 -19
  26. package/docs/theme.mjs +3 -3
  27. package/editor/icons.d.ts +3 -0
  28. package/editor/index.js +4 -1
  29. package/package.json +2 -2
  30. package/styles/ui-button.css +2 -14
  31. package/styles/ui-card.css +1 -1
  32. package/styles/ui-checkbox.css +0 -9
  33. package/styles/ui-color-picker.css +0 -13
  34. package/styles/ui-combobox.css +2 -28
  35. package/styles/ui-cycle-button.css +36 -0
  36. package/styles/ui-dialog.css +2 -2
  37. package/styles/ui-icon-button.css +6 -13
  38. package/styles/ui-input-group.css +0 -13
  39. package/styles/ui-input.css +2 -18
  40. package/styles/ui-menu.css +1 -1
  41. package/styles/ui-popover.css +1 -1
  42. package/styles/ui-radio.css +0 -9
  43. package/styles/ui-select.css +3 -10
  44. package/styles/ui-switch.css +0 -9
  45. package/styles/ui-tooltip.css +1 -1
  46. package/theme-system.css +14 -14
  47. package/tokens.css +2 -10
  48. package/vanilla/UiCycleButton.d.ts +29 -0
  49. package/vanilla/UiCycleButton.js +85 -0
  50. package/vanilla/UiIcon.js +1 -1
  51. package/vanilla/index.js +1 -0
  52. package/vue/UiButton.vue +2 -11
  53. package/vue/UiCard.vue +5 -3
  54. package/vue/UiCheckbox.vue +0 -6
  55. package/vue/UiColorPicker.vue +0 -9
  56. package/vue/UiCombobox.vue +2 -26
  57. package/vue/UiCycleButton.d.ts +69 -0
  58. package/vue/UiCycleButton.js +2 -0
  59. package/vue/UiCycleButton.vue +403 -0
  60. package/vue/UiDialog.vue +6 -2
  61. package/vue/UiIcon.vue +22 -0
  62. package/vue/UiIconButton.vue +2 -16
  63. package/vue/UiInput.vue +1 -22
  64. package/vue/UiInputGroup.vue +0 -10
  65. package/vue/UiMenu.vue +3 -1
  66. package/vue/UiPopover.vue +3 -1
  67. package/vue/UiRadio.vue +0 -6
  68. package/vue/UiSelect.vue +2 -8
  69. package/vue/UiSwitch.vue +0 -6
  70. package/vue/UiTooltip.vue +3 -1
  71. package/vue/chunks/UiAffordanceScope.js +1 -1
  72. package/vue/chunks/UiAvatar.js +1 -1
  73. package/vue/chunks/UiAvatarGroup.js +1 -1
  74. package/vue/chunks/UiBadge.js +1 -1
  75. package/vue/chunks/UiBreadcrumbItem.js +1 -1
  76. package/vue/chunks/UiBreadcrumbs.js +1 -1
  77. package/vue/chunks/UiButton.js +3 -3
  78. package/vue/chunks/UiCallout.js +1 -1
  79. package/vue/chunks/UiCard.js +4 -4
  80. package/vue/chunks/UiCheckbox.js +2 -2
  81. package/vue/chunks/UiCluster.js +1 -1
  82. package/vue/chunks/UiColorPicker.js +2 -2
  83. package/vue/chunks/UiCombobox.js +2 -2
  84. package/vue/chunks/UiContainer.js +1 -1
  85. package/vue/chunks/UiContextMenu.js +1 -1
  86. package/vue/chunks/UiCover.js +1 -1
  87. package/vue/chunks/UiCurveEditor.js +1 -1
  88. package/vue/chunks/UiCycleButton.js +352 -0
  89. package/vue/chunks/UiDescriptionItem.js +1 -1
  90. package/vue/chunks/UiDescriptionList.js +1 -1
  91. package/vue/chunks/UiDialog.js +2 -2
  92. package/vue/chunks/UiDisclosure.js +1 -1
  93. package/vue/chunks/UiEditable.js +1 -1
  94. package/vue/chunks/UiEditorBlockActions.js +1 -1
  95. package/vue/chunks/UiEditorChipEditor.js +1 -1
  96. package/vue/chunks/UiEditorCodeLanguage.js +1 -1
  97. package/vue/chunks/UiEditorFootnoteEditor.js +1 -1
  98. package/vue/chunks/UiEditorImageToolbar.js +1 -1
  99. package/vue/chunks/UiEditorInsertTableGrid.js +1 -1
  100. package/vue/chunks/UiEditorLinkCard.js +1 -1
  101. package/vue/chunks/UiEditorLinkForm.js +1 -1
  102. package/vue/chunks/UiEditorMentionMenu.js +1 -1
  103. package/vue/chunks/UiEditorMentionMenuItem.js +1 -1
  104. package/vue/chunks/UiEditorSlashMenu.js +1 -1
  105. package/vue/chunks/UiEditorSlashMenuGroup.js +1 -1
  106. package/vue/chunks/UiEditorSlashMenuItem.js +1 -1
  107. package/vue/chunks/UiEditorSurface.js +1 -1
  108. package/vue/chunks/UiEditorTableContextMenu.js +1 -1
  109. package/vue/chunks/UiEditorTableOverlay.js +1 -1
  110. package/vue/chunks/UiEditorTableToolbar.js +1 -1
  111. package/vue/chunks/UiEditorToolbar.js +1 -1
  112. package/vue/chunks/UiFloatingActionButton.js +1 -1
  113. package/vue/chunks/UiFormField.js +1 -1
  114. package/vue/chunks/UiGrid.js +1 -1
  115. package/vue/chunks/UiIcon.js +70 -2
  116. package/vue/chunks/UiIconButton.js +4 -4
  117. package/vue/chunks/UiImage.js +1 -1
  118. package/vue/chunks/UiInput.js +2 -2
  119. package/vue/chunks/UiInputGroup.js +2 -2
  120. package/vue/chunks/UiLightEditor.js +1 -1
  121. package/vue/chunks/UiList.js +1 -1
  122. package/vue/chunks/UiListItem.js +1 -1
  123. package/vue/chunks/UiListbox.js +1 -1
  124. package/vue/chunks/UiMenu.js +2 -2
  125. package/vue/chunks/UiMenuGroup.js +1 -1
  126. package/vue/chunks/UiMenuItem.js +1 -1
  127. package/vue/chunks/UiMeter.js +1 -1
  128. package/vue/chunks/UiNavItem.js +1 -1
  129. package/vue/chunks/UiNumberInput.js +1 -1
  130. package/vue/chunks/UiPageHeader.js +1 -1
  131. package/vue/chunks/UiPopover.js +2 -2
  132. package/vue/chunks/UiRadio.js +2 -2
  133. package/vue/chunks/UiRadioGroup.js +1 -1
  134. package/vue/chunks/UiReel.js +1 -1
  135. package/vue/chunks/UiScrollArea.js +1 -1
  136. package/vue/chunks/UiSearchResultRow.js +1 -1
  137. package/vue/chunks/UiSearchShell.js +1 -1
  138. package/vue/chunks/UiSection.js +1 -1
  139. package/vue/chunks/UiSelect.js +2 -2
  140. package/vue/chunks/UiSeparator.js +1 -1
  141. package/vue/chunks/UiSidebar.js +1 -1
  142. package/vue/chunks/UiSlider.js +1 -1
  143. package/vue/chunks/UiSpinner.js +1 -1
  144. package/vue/chunks/UiStack.js +1 -1
  145. package/vue/chunks/UiStatusMessage.js +1 -1
  146. package/vue/chunks/UiSwatch.js +1 -1
  147. package/vue/chunks/UiSwitch.js +2 -2
  148. package/vue/chunks/UiSwitcher.js +1 -1
  149. package/vue/chunks/UiTable.js +1 -1
  150. package/vue/chunks/UiTabs.js +1 -1
  151. package/vue/chunks/UiText.js +1 -1
  152. package/vue/chunks/UiTextarea.js +1 -1
  153. package/vue/chunks/UiToast.js +1 -1
  154. package/vue/chunks/UiToastRegion.js +1 -1
  155. package/vue/chunks/UiTooltip.js +2 -2
  156. package/vue/chunks/UiTree.js +1 -1
  157. package/vue/chunks/UiTreeItem.js +1 -1
  158. package/vue/chunks/props.js +12 -1
  159. package/vue/components.css +74 -179
  160. package/vue/editor/index.js +68 -0
  161. package/vue/index.d.ts +1 -0
  162. package/vue/index.js +2 -1
package/docs/README.md CHANGED
@@ -17,7 +17,7 @@ pnpm exec looma-docs init --title "The Fellowship"
17
17
  pnpm exec looma-docs dev
18
18
  ```
19
19
 
20
- Use the tool versions accepted by Looma's optional peer dependencies (Markupress 0.8.5 or newer on the supported 0.8 line). Regular component imports do not load the documentation server. Vue and Tiptap remain optional; install them when your own authored documentation mounts the Vue editor.
20
+ Use the tool versions accepted by Looma's optional peer dependencies (Markupress 0.8.7 or newer on the supported 0.8 line). Regular component imports do not load the documentation server. Vue and Tiptap remain optional; install them when your own authored documentation mounts the Vue editor.
21
21
 
22
22
  Initialization creates three project-owned files:
23
23
 
@@ -38,11 +38,43 @@ pnpm exec looma-docs preview
38
38
 
39
39
  `build` produces a static HTMLKit site in the configured output directory. `preview` serves that output without a repository writer. All commands accept `--root`; `dev` and `preview` also accept `--port`.
40
40
 
41
+ ## Name your local design system
42
+
43
+ `looma-docs init --title "Fellowship UI"` sets your site's name. To rename an
44
+ existing local documentation site, edit `title` in `looma.docs.json`; the local
45
+ server rebuilds the header, generated landing page and page titles with that
46
+ name. You do not need to fork Looma or change component imports.
47
+
48
+ You can also supply your logo and navigation:
49
+
50
+ ```json
51
+ {
52
+ "title": "Fellowship UI",
53
+ "logo": {
54
+ "light": "/assets/fellowship-light.svg",
55
+ "dark": "/assets/fellowship-dark.svg"
56
+ },
57
+ "nav": [{ "label": "Our application", "href": "https://example.com" }]
58
+ }
59
+ ```
60
+
61
+ Merge these fields into your existing configuration. Serve the logo URLs from
62
+ your application's public assets, such as `public/assets/`. Leave `logo` unset
63
+ for a text-only brand. To author your own introduction, set `content` to your
64
+ Markdown directory and add its `index.md`; it replaces the generated landing
65
+ page without replacing component reference pages.
66
+
67
+ The site name is yours. Component ownership stays accurate: built-in cards
68
+ still identify `@threadlabs/looma`, while your discovered components identify
69
+ the `packageName` you configure below. That is package attribution, not the
70
+ name of your design system. Shared CSS and recipe filenames are configured
71
+ under `theme`; changing a site title does not require renaming those files.
72
+
41
73
  ## Change the rule, keep the family consistent
42
74
 
43
75
  The authoring model groups shared decisions as Colour, Type, Size & spacing, Material and Motion. The controls expose shared intent: control roominess relative to text, spacing, corners, inner and outer depth, and response pace.
44
76
 
45
- The generated values follow rules. The medium control height is base text size times control roominess; small and large heights follow the size ratio. On touch, small rises to at least the comfortable 44px floor and medium/large grow from it, preserving the ratio. Spacing derives from base text independently of control roominess and follows its own curve: linear at 1, increasingly curved toward 2, with stable endpoints so the largest gaps do not grow without bound. Depth generates families of shadows; it does not add motion amplitude. Fields and buttons with the same size setting use the same box height, while content can still grow when it needs more room.
77
+ The generated values follow rules. The medium control height is base text size times control roominess; small and large heights follow the size ratio. Touch does not inflate the visible size ladder. Hit-area guidance is contextual and adjustable; 44px is a comfort preference for expanded targets, not a mandatory visible height. Invisible targets must not steal neighbouring controls. Spacing derives from base text independently of control roominess and follows its own curve: linear at 1, increasingly curved toward 2, with stable endpoints so the largest gaps do not grow without bound. Depth generates families of shadows; it does not add motion amplitude. Fields and buttons with the same size setting use the same box height, while content can still grow when it needs more room.
46
78
 
47
79
  Choose a coordinated palette, then refine surface warmth, color intensity and palette relationship. Warmth runs from cool to warm paper and ink, with 0.5 neutral; intensity runs from quiet to vivid, with 0.5 retaining original seed intensity. Tonal keeps seed hues; neighboring and complementary relate quiet hues to the action hue. These decisions regenerate paired light/dark colors through Oklab/OKLCH operations, gamut repair and readability checks. They do not transform existing CSS colors live. Four color seeds remain available for precise edits; the light/dark range previews and resolved values explain adjustments.
48
80
 
@@ -56,7 +88,7 @@ Number controls accept arithmetic such as `1rem / 1.2` or `150ms * 2`, calculate
56
88
 
57
89
  Lengths retain their CSS units, such as `rem`; times retain `ms` or `s`. The downloaded CSS includes the canonical shared CSS derivation rules as well as resolved theme values. Download JSON separately to retain editable decisions and font/preset provenance. Import a downloaded JSON recipe to continue editing it. Keep hand-written CSS in a separate overrides file so regenerating the theme does not replace it.
58
90
 
59
- Undo and Redo move through accepted recipe changes. **Compare entry theme** temporarily previews the theme present when this customizer opened; it does not replace your edits. **Explore nearby styles** previews Warmer, Quieter, More expressive or More compact before you apply it. Palette, Material and Type & spacing locks preserve those families during variations. Cancel preview returns to the current recipe.
91
+ Undo and Redo move through accepted recipe changes. There is no page-entry comparison state: reopening the customizer does not create a hidden before/after reference. **Explore nearby styles** previews Warmer, Quieter, More expressive or More compact before you apply it. Palette, Material and Type & spacing locks preserve those families during variations. Cancel preview returns to the current recipe.
60
92
 
61
93
  Changes apply throughout the documentation and remain in this browser across navigation and reload until reset. Repository files change only when you save locally. Starting points update the shell and all selected cards. Reset returns to the repository baseline. Invalid input keeps the last valid preview and explains which decision needs attention.
62
94
 
@@ -90,7 +122,7 @@ An optional recipe `fonts` mapping records Google family names by `body-font` an
90
122
 
91
123
  Light position is relative to each surface: horizontal and vertical positions run from 0 to 1, with 0.5, 0 at top centre. Drag the source, use its arrow keys (Shift moves faster), or enter exact percentages. Home returns it to top centre. Spread runs from a concentrated spot to broad illumination; strength controls its intensity. The source influences reflections, illuminated edges and the direction of depth shadows throughout the theme.
92
124
 
93
- Depth controls elevation and recession independently from finish, transmission and motion. Finish controls how strongly a material catches a reflection, from matte to polished; it needs nonzero light strength. Light spread shapes that reflection, while position also directs shadows. A light strength of zero does not remove depth shadows. Edge light catches its boundary; glow spills softly beyond it. Glassiness and surface separation govern the decorative material around floating content, scaled by popup purpose and rendered size. The content plane stays opaque. Overlay translucency changes the shared overlay fill and decorative glass; it does not turn material content transparent. Separation can create a rim even with glassiness at zero. Reflection peaks are bounded for readable text, and reduced transparency disables the ornamental reflection and glow as well as backdrop blur.
125
+ Depth controls elevation and recession independently from finish, transmission and motion. Finish controls how strongly a material catches a reflection, from matte to polished; it needs nonzero light strength. Light spread shapes that reflection, while position also directs shadows. A light strength of zero does not remove depth shadows. Edge light catches its boundary; glow spills beyond it. Glassiness and glow use a gently increasing lower range and an expressive upper response (`g + 2g⁴`), so the maximum can be exuberant without raising restrained defaults. Glassiness and surface separation govern the decorative material around floating content, scaled by popup purpose and rendered size. The content plane stays opaque. Overlay translucency changes the shared overlay fill and decorative glass; it does not turn material content transparent. Separation can create a rim even with glassiness at zero. Reflection peaks are bounded for readable text, and reduced transparency disables the ornamental reflection and glow as well as backdrop blur.
94
126
 
95
127
  Card keeps surface structure and decorative treatment independent:
96
128
 
@@ -128,9 +160,16 @@ Use the public semantic color properties inside those regions. A floating surfac
128
160
 
129
161
  ## Add your components
130
162
 
131
- Build your design-system components in HTML Next. Their definitions, description attributes, controllers and HTML examples are the source for the generated documentation and customizer cards. Use Looma’s public semantic properties to make your components respond to the same theme.
163
+ Build your design-system components in HTML Next. Their definitions, description attributes, modules and HTML examples are the source for the generated documentation and customizer cards. Use Looma’s public semantic properties to make your components respond to the same theme.
164
+
165
+ 1. Create a directory for your HTML Next component resources, such as `app/components`.
166
+ 2. Opt that directory into `components` in `looma.docs.json`, with your package's name.
167
+ 3. Put `docs.json` and an `examples/` directory beside each resource, as shown below.
168
+ 4. Run `looma-docs dev`. Your components appear beside Looma's in the reference,
169
+ search and customizer example picker. Resources under registered directories
170
+ are rediscovered when the local server rebuilds.
132
171
 
133
- Opt into source directories in `looma.docs.json`:
172
+ The configuration can combine your name, authored guides and discovery roots:
134
173
 
135
174
  ```json
136
175
  {
package/docs/card.html CHANGED
@@ -8,6 +8,7 @@
8
8
  <prop name="title" type="string" default="Example">The documented component name.</prop>
9
9
  <prop name="description" type="string" default="">The component's authored description.</prop>
10
10
  <prop name="exampleTitle" type="string" default="">The selected authored example.</prop>
11
+ <prop name="previewOnly" type="boolean" default="false">Show only the live example, for compact theme samplers whose host supplies the heading.</prop>
11
12
  <prop name="reference" type="boolean" default="false">Use the spacious full-reference example presentation.</prop>
12
13
  <prop name="src" type="string" default="">The compiled isolated example page.</prop>
13
14
  <prop name="resizable" type="boolean" default="false">Expose a width control for responsive examples.</prop>
@@ -24,7 +25,7 @@
24
25
  <div class="preview"><iframe $ref="frame" from:title="$title" from:src="$src" sandbox="allow-scripts" from:loading="$loading" width="100%" height="208"></iframe></div>
25
26
  <section $if="$reference" class="example-code"><h4>Code</h4><slot name="code"></slot></section>
26
27
  <div $if="$resizable" class="preview-width"> <ui-form-field><label slot="label">Preview width</label><span slot="help">Explore the example at a narrower width.</span><ui-input $ref="width" type="number" min="240" max="1600" value="720" aria-label="Preview width in pixels" size="sm"></ui-input></ui-form-field></div>
27
- <div $if="not $reference" class="body"><ui-stack gap="xs">
28
+ <div $if="not $reference and not $previewOnly" class="body"><ui-stack gap="xs">
28
29
  <div class="meta"><span>{$category}</span><code>{$nativeRoot}</code></div>
29
30
  <h3><a from:href="$href">{$title}</a></h3>
30
31
  <p class="description">{$description}</p>
package/docs/catalog.json CHANGED
@@ -90,6 +90,11 @@
90
90
  "package": "@threadlabs/looma",
91
91
  "resource": "@threadlabs/looma/components/ui-curve-editor/ui-curve-editor.html"
92
92
  },
93
+ {
94
+ "tag": "ui-cycle-button",
95
+ "package": "@threadlabs/looma",
96
+ "resource": "@threadlabs/looma/components/ui-cycle-button/ui-cycle-button.html"
97
+ },
93
98
  {
94
99
  "tag": "ui-description-item",
95
100
  "package": "@threadlabs/looma",
@@ -443,7 +448,7 @@
443
448
  ],
444
449
  "schemaVersion": 1,
445
450
  "package": "@threadlabs/looma",
446
- "version": "0.37.0",
451
+ "version": "0.37.1",
447
452
  "groups": [
448
453
  {
449
454
  "id": "layout",
@@ -2529,7 +2534,7 @@
2529
2534
  {
2530
2535
  "name": "size",
2531
2536
  "property": "size",
2532
- "description": "Height, padding, and type size. xs is 1.5rem with the smallest text, for a control inside a line of small text; sm is the small control height (2rem by default) with tighter padding and smaller text, md is the standard control height (2.5rem), and lg is taller (3rem) with roomier padding. Under touch, sm grows to the touch minimum, while xs keeps its size inside its line and gains an invisible touch-sized hit area.",
2537
+ "description": "Height, padding, and type size. xs is 1.5rem with the smallest text, for a control inside a line of small text; sm is the small control height (2rem by default) with tighter padding and smaller text, md is the standard control height (2.5rem), and lg is taller (3rem) with roomier padding. Visible sizes stay the same on touch. Inline xs and link actions can gain an adjustable invisible hit area.",
2533
2538
  "type": "keyword",
2534
2539
  "default": "md",
2535
2540
  "options": [
@@ -2670,7 +2675,7 @@
2670
2675
  },
2671
2676
  {
2672
2677
  "name": "size",
2673
- "description": "Height, padding, and type size. xs is 1.5rem with the smallest text, for a control inside a line of small text; sm is the small control height (2rem by default) with tighter padding and smaller text, md is the standard control height (2.5rem), and lg is taller (3rem) with roomier padding. Under touch, sm grows to the touch minimum, while xs keeps its size inside its line and gains an invisible touch-sized hit area.",
2678
+ "description": "Height, padding, and type size. xs is 1.5rem with the smallest text, for a control inside a line of small text; sm is the small control height (2rem by default) with tighter padding and smaller text, md is the standard control height (2.5rem), and lg is taller (3rem) with roomier padding. Visible sizes stay the same on touch. Inline xs and link actions can gain an adjustable invisible hit area.",
2674
2679
  "type": "\"xs\" | \"sm\" | \"md\" | \"lg\"",
2675
2680
  "default": "md",
2676
2681
  "options": [
@@ -3543,9 +3548,6 @@
3543
3548
  {
3544
3549
  "name": "--ui-control-inset"
3545
3550
  },
3546
- {
3547
- "name": "--ui-control-min-block-size"
3548
- },
3549
3551
  {
3550
3552
  "name": "--ui-control-size-lg"
3551
3553
  },
@@ -4167,6 +4169,16 @@
4167
4169
  "resizable": false,
4168
4170
  "bindings": "",
4169
4171
  "frameworks": {}
4172
+ },
4173
+ {
4174
+ "id": "@threadlabs/looma/ui-cluster/99-theme-sampler",
4175
+ "name": "99-theme-sampler",
4176
+ "title": "Theme sampler",
4177
+ "description": "A compact collection of real controls and a surface for judging a shared theme together.",
4178
+ "markup": "<ui-card treatment=\"expressive\" padding=\"sm\"><ui-stack gap=\"xs\">\n <ui-cluster gap=\"xs\" justify=\"between\"><ui-text variant=\"eyebrow\">Fellowship office</ui-text><ui-badge tone=\"success\">Ready</ui-badge></ui-cluster>\n <ui-cluster gap=\"xs\"><ui-button size=\"sm\" preset=\"primary\">Send memo</ui-button><ui-button size=\"sm\" preset=\"standard\">Save draft</ui-button><ui-button\n size=\"sm\"\n variant=\"outline\"\n >Archive</ui-button><ui-cycle-button\n id=\"sampler-availability\"\n size=\"sm\"\n label=\"Availability\"\n items='[{\"value\":\"available\",\"label\":\"Available\",\"icon\":\"circle-check\"},{\"value\":\"busy\",\"label\":\"Busy\",\"icon\":\"circle-dot\"},{\"value\":\"away\",\"label\":\"Away\",\"icon\":\"moon\"}]'\n ></ui-cycle-button></ui-cluster>\n <ui-input-group><ui-icon slot=\"prefix\" name=\"search\"></ui-icon><ui-input\n size=\"sm\"\n aria-label=\"Search office\"\n placeholder=\"Search Fellowship office\"\n ></ui-input></ui-input-group>\n <ui-cluster gap=\"xs\"><ui-checkbox size=\"sm\" checked>Notify Sam</ui-checkbox><ui-switch size=\"sm\" checked>Available</ui-switch><ui-badge tone=\"accent\"\n >Draft</ui-badge></ui-cluster>\n </ui-stack></ui-card>",
4179
+ "resizable": false,
4180
+ "bindings": "items=availabilityStates",
4181
+ "frameworks": {}
4170
4182
  }
4171
4183
  ]
4172
4184
  },
@@ -4217,9 +4229,6 @@
4217
4229
  "var(--ui-space-1)"
4218
4230
  ]
4219
4231
  },
4220
- {
4221
- "name": "--ui-control-min-block-size"
4222
- },
4223
4232
  {
4224
4233
  "name": "--ui-control-size-lg"
4225
4234
  },
@@ -4487,9 +4496,7 @@
4487
4496
  "declarations": [
4488
4497
  "var(--ui-control-size-md)",
4489
4498
  "var(--ui-control-size-sm)",
4490
- "var(--ui-control-size-lg)",
4491
- "max(var(--ui-control-size-md), var(--ui-control-min-block-size))",
4492
- "max(var(--ui-control-size-sm), var(--ui-control-min-block-size))"
4499
+ "var(--ui-control-size-lg)"
4493
4500
  ]
4494
4501
  },
4495
4502
  {
@@ -4608,9 +4615,6 @@
4608
4615
  "var(--ui-space-1)"
4609
4616
  ]
4610
4617
  },
4611
- {
4612
- "name": "--ui-control-min-block-size"
4613
- },
4614
4618
  {
4615
4619
  "name": "--ui-control-size-lg"
4616
4620
  },
@@ -6847,6 +6851,194 @@
6847
6851
  }
6848
6852
  ]
6849
6853
  },
6854
+ {
6855
+ "tag": "ui-cycle-button",
6856
+ "package": "@threadlabs/looma",
6857
+ "root": "span",
6858
+ "description": "One compact native button that cycles an ordered set of named icon states without moving focus.",
6859
+ "designTokens": {
6860
+ "sources": [
6861
+ "packages/looma/src/components/ui-cycle-button/ui-cycle-button.html"
6862
+ ],
6863
+ "component": [],
6864
+ "shared": []
6865
+ },
6866
+ "attributes": [
6867
+ {
6868
+ "name": "disabled",
6869
+ "property": "disabled",
6870
+ "description": "Prevents cycling while keeping the selected state visible.",
6871
+ "type": "boolean",
6872
+ "default": false
6873
+ },
6874
+ {
6875
+ "name": "index",
6876
+ "property": "index",
6877
+ "description": "Initial or externally selected zero-based state. The first state is the default.",
6878
+ "type": "number",
6879
+ "default": 0
6880
+ },
6881
+ {
6882
+ "name": "items",
6883
+ "property": "items",
6884
+ "description": "Ordered states with a value, readable label and Looma icon name. Activation advances to the next state and wraps.",
6885
+ "type": "list(object)",
6886
+ "fields": [
6887
+ {
6888
+ "path": "[].value",
6889
+ "type": "string",
6890
+ "required": true
6891
+ },
6892
+ {
6893
+ "path": "[].label",
6894
+ "type": "string",
6895
+ "required": true
6896
+ },
6897
+ {
6898
+ "path": "[].icon",
6899
+ "type": "string",
6900
+ "required": true
6901
+ }
6902
+ ]
6903
+ },
6904
+ {
6905
+ "name": "label",
6906
+ "property": "label",
6907
+ "description": "What the button changes, such as Appearance. The selected state is included in its accessible name.",
6908
+ "type": "string",
6909
+ "default": "State"
6910
+ },
6911
+ {
6912
+ "name": "size",
6913
+ "property": "size",
6914
+ "description": "Uses IconButton's compact visible sizes and adjustable invisible touch area.",
6915
+ "type": "keyword",
6916
+ "default": "md",
6917
+ "options": [
6918
+ "sm",
6919
+ "md",
6920
+ "lg"
6921
+ ]
6922
+ },
6923
+ {
6924
+ "name": "variant",
6925
+ "property": "variant",
6926
+ "description": "Uses IconButton's standard emphasis.",
6927
+ "type": "keyword",
6928
+ "default": "outline",
6929
+ "options": [
6930
+ "ghost",
6931
+ "outline",
6932
+ "solid"
6933
+ ]
6934
+ }
6935
+ ],
6936
+ "properties": [
6937
+ {
6938
+ "name": "disabled",
6939
+ "description": "Prevents cycling while keeping the selected state visible.",
6940
+ "type": "boolean",
6941
+ "default": false
6942
+ },
6943
+ {
6944
+ "name": "index",
6945
+ "description": "Initial or externally selected zero-based state. The first state is the default.",
6946
+ "type": "number",
6947
+ "default": 0
6948
+ },
6949
+ {
6950
+ "name": "items",
6951
+ "description": "Ordered states with a value, readable label and Looma icon name. Activation advances to the next state and wraps.",
6952
+ "type": "readonly ({ readonly value: string; readonly label: string; readonly icon: string })[]",
6953
+ "fields": [
6954
+ {
6955
+ "path": "[].value",
6956
+ "type": "string",
6957
+ "required": true
6958
+ },
6959
+ {
6960
+ "path": "[].label",
6961
+ "type": "string",
6962
+ "required": true
6963
+ },
6964
+ {
6965
+ "path": "[].icon",
6966
+ "type": "string",
6967
+ "required": true
6968
+ }
6969
+ ]
6970
+ },
6971
+ {
6972
+ "name": "label",
6973
+ "description": "What the button changes, such as Appearance. The selected state is included in its accessible name.",
6974
+ "type": "string",
6975
+ "default": "State"
6976
+ },
6977
+ {
6978
+ "name": "size",
6979
+ "description": "Uses IconButton's compact visible sizes and adjustable invisible touch area.",
6980
+ "type": "\"sm\" | \"md\" | \"lg\"",
6981
+ "default": "md",
6982
+ "options": [
6983
+ "sm",
6984
+ "md",
6985
+ "lg"
6986
+ ]
6987
+ },
6988
+ {
6989
+ "name": "variant",
6990
+ "description": "Uses IconButton's standard emphasis.",
6991
+ "type": "\"ghost\" | \"outline\" | \"solid\"",
6992
+ "default": "outline",
6993
+ "options": [
6994
+ "ghost",
6995
+ "outline",
6996
+ "solid"
6997
+ ]
6998
+ }
6999
+ ],
7000
+ "events": [
7001
+ {
7002
+ "name": "change",
7003
+ "detailType": "{ readonly value: string; readonly index: number }",
7004
+ "detailSchema": "object",
7005
+ "fields": [
7006
+ {
7007
+ "path": "value",
7008
+ "type": "string",
7009
+ "required": true
7010
+ },
7011
+ {
7012
+ "path": "index",
7013
+ "type": "number",
7014
+ "required": true
7015
+ }
7016
+ ],
7017
+ "detailDocs": "Emitted when a toggleable control changes checked state."
7018
+ }
7019
+ ],
7020
+ "slots": [],
7021
+ "group": "forms",
7022
+ "id": "@threadlabs/looma/ui-cycle-button",
7023
+ "title": "Cycle Button",
7024
+ "resource": "@threadlabs/looma/components/ui-cycle-button/ui-cycle-button.html",
7025
+ "targets": [
7026
+ "html-next",
7027
+ "vue"
7028
+ ],
7029
+ "examples": [
7030
+ {
7031
+ "id": "@threadlabs/looma/ui-cycle-button/01-appearance",
7032
+ "name": "01-appearance",
7033
+ "title": "Cycling appearance",
7034
+ "description": "One native button cycles Automatic, Light and Dark while preserving keyboard focus.",
7035
+ "markup": "<ui-cluster gap=\"s\"><ui-text>Appearance</ui-text><ui-cycle-button\n id=\"appearance-cycle\"\n label=\"Appearance\"\n items='[{\"value\":\"automatic\",\"label\":\"Automatic\",\"icon\":\"monitor\"},{\"value\":\"light\",\"label\":\"Light\",\"icon\":\"sun\"},{\"value\":\"dark\",\"label\":\"Dark\",\"icon\":\"moon\"}]'\n ></ui-cycle-button></ui-cluster>",
7036
+ "resizable": false,
7037
+ "bindings": "items=appearanceStates",
7038
+ "frameworks": {}
7039
+ }
7040
+ ]
7041
+ },
6850
7042
  {
6851
7043
  "tag": "ui-description-item",
6852
7044
  "package": "@threadlabs/looma",
@@ -12790,7 +12982,7 @@
12790
12982
  {
12791
12983
  "name": "match-button",
12792
12984
  "property": "matchButton",
12793
- "description": "Matches a boxed Button of the same size when the two share a flex row. Uses the control-size theme tokens and stretches to the row's height, including touch-sized controls. Leave it off for compact toolbar and inline icon buttons.",
12985
+ "description": "Matches a boxed Button of the same size when the two share a flex row. Uses the control-size theme tokens and stretches to the row's height, on mouse and touch alike. Leave it off for compact toolbar and inline icon buttons.",
12794
12986
  "type": "boolean",
12795
12987
  "default": false
12796
12988
  },
@@ -12832,7 +13024,7 @@
12832
13024
  {
12833
13025
  "name": "size",
12834
13026
  "property": "size",
12835
- "description": "Button and icon size together: sm is 1.75rem with a small icon, md is 2rem, and lg is 2.5rem with a large icon. On touch, every size gets an invisible hit area of at least 44px.",
13027
+ "description": "Button and icon size together: sm is 1.75rem with a small icon, md is 2rem, and lg is 2.5rem with a large icon. Touch can use an invisible hit area sized by the adjustable control target preference.",
12836
13028
  "type": "keyword",
12837
13029
  "default": "md",
12838
13030
  "options": [
@@ -12906,7 +13098,7 @@
12906
13098
  },
12907
13099
  {
12908
13100
  "name": "matchButton",
12909
- "description": "Matches a boxed Button of the same size when the two share a flex row. Uses the control-size theme tokens and stretches to the row's height, including touch-sized controls. Leave it off for compact toolbar and inline icon buttons.",
13101
+ "description": "Matches a boxed Button of the same size when the two share a flex row. Uses the control-size theme tokens and stretches to the row's height, on mouse and touch alike. Leave it off for compact toolbar and inline icon buttons.",
12910
13102
  "type": "boolean",
12911
13103
  "default": false
12912
13104
  },
@@ -12943,7 +13135,7 @@
12943
13135
  },
12944
13136
  {
12945
13137
  "name": "size",
12946
- "description": "Button and icon size together: sm is 1.75rem with a small icon, md is 2rem, and lg is 2.5rem with a large icon. On touch, every size gets an invisible hit area of at least 44px.",
13138
+ "description": "Button and icon size together: sm is 1.75rem with a small icon, md is 2rem, and lg is 2.5rem with a large icon. Touch can use an invisible hit area sized by the adjustable control target preference.",
12947
13139
  "type": "\"sm\" | \"md\" | \"lg\"",
12948
13140
  "default": "md",
12949
13141
  "options": [
@@ -13376,9 +13568,7 @@
13376
13568
  "fallbacks": [
13377
13569
  "var(--ui-control-size-md)",
13378
13570
  "var(--ui-control-size-sm)",
13379
- "var(--ui-control-size-lg)",
13380
- "max(var(--ui-control-size-md), var(--ui-control-min-block-size))",
13381
- "max(var(--ui-control-size-sm), var(--ui-control-min-block-size))"
13571
+ "var(--ui-control-size-lg)"
13382
13572
  ]
13383
13573
  },
13384
13574
  {
@@ -13452,9 +13642,6 @@
13452
13642
  "var(--ui-space-1)"
13453
13643
  ]
13454
13644
  },
13455
- {
13456
- "name": "--ui-control-min-block-size"
13457
- },
13458
13645
  {
13459
13646
  "name": "--ui-control-placeholder",
13460
13647
  "fallbacks": [
@@ -13822,9 +14009,6 @@
13822
14009
  {
13823
14010
  "name": "--ui-control-inset"
13824
14011
  },
13825
- {
13826
- "name": "--ui-control-min-block-size"
13827
- },
13828
14012
  {
13829
14013
  "name": "--ui-control-size-lg"
13830
14014
  },
@@ -17415,9 +17599,6 @@
17415
17599
  {
17416
17600
  "name": "--ui-choice-mark-size"
17417
17601
  },
17418
- {
17419
- "name": "--ui-control-min-block-size"
17420
- },
17421
17602
  {
17422
17603
  "name": "--ui-control-size-lg"
17423
17604
  },
@@ -19139,9 +19320,6 @@
19139
19320
  "var(--ui-space-1)"
19140
19321
  ]
19141
19322
  },
19142
- {
19143
- "name": "--ui-control-min-block-size"
19144
- },
19145
19323
  {
19146
19324
  "name": "--ui-control-size-lg"
19147
19325
  },
@@ -20767,9 +20945,6 @@
20767
20945
  {
20768
20946
  "name": "--ui-control-inset"
20769
20947
  },
20770
- {
20771
- "name": "--ui-control-min-block-size"
20772
- },
20773
20948
  {
20774
20949
  "name": "--ui-control-size-lg"
20775
20950
  },
@@ -11,9 +11,9 @@ export function customizerResource(site, input = defaultRecipe()) {
11
11
  const toCard = example => Object.fromEntries(cardFields.map(key => [key, example[key]]));
12
12
  const initial = ['ui-button', 'ui-input', 'ui-color-picker', 'ui-checkbox', 'ui-card', 'ui-switch', 'ui-text', 'ui-dialog', 'ui-popover'].map(tag => examples.find(example => example.tag === tag && (tag !== 'ui-button' || example.id.endsWith('/02-variant')) && (tag !== 'ui-text' || example.id.endsWith('/02-variants')) && (tag !== 'ui-card' || example.id.endsWith('/04-expressive-surface'))) ?? examples.find(example => example.tag === tag)).filter(Boolean).map(toCard);
13
13
  if (!initial.length && examples.length) initial.push(toCard(examples[0]));
14
- const documentationCard = (tag, suffix) => {
14
+ const documentationCard = (tag, suffix, previewOnly = false) => {
15
15
  const example = examples.find(item => item.tag === tag && (!suffix || item.id.endsWith(suffix))) ?? examples.find(item => item.tag === tag);
16
- return example ? `<looma-example-card ${cardFields.filter(key => key !== 'id').map(key => `${key.replace(/[A-Z]/g, letter => '-' + letter.toLowerCase())}="${escape(String(example[key]))}"`).join(' ')} identity="${escape(example.id)}" loading="lazy"></looma-example-card>` : '';
16
+ return example ? `<looma-example-card ${previewOnly ? 'preview-only' : ''} ${cardFields.filter(key => key !== 'id').map(key => `${key.replace(/[A-Z]/g, letter => '-' + letter.toLowerCase())}="${escape(String(example[key]))}"`).join(' ')} identity="${escape(example.id)}" loading="lazy"></looma-example-card>` : '';
17
17
  };
18
18
  const renderField = field => {
19
19
  const value = recipe.values[field.id], editor = valueEditor(field, value, { remPixels: parseFloat(recipe.values['text-size']) });
@@ -45,7 +45,7 @@ export function customizerResource(site, input = defaultRecipe()) {
45
45
  'Colour': `${renderPreset('palette', 'overview')}<ui-text variant="caption">Coordinated light and dark ranges.</ui-text><ui-button data-action="refine-palette">Refine palette</ui-button>`,
46
46
  'Material': `${renderPreset('material', 'overview')}<ui-text variant="caption">Material and light, shared everywhere.</ui-text><ui-button data-action="refine-material">Refine material & light</ui-button>`,
47
47
  'Motion': `<ui-text variant="caption">Open and close this real documentation example to feel the response pace and curve. Reduced motion follows your system preference.</ui-text>${documentationCard('ui-popover')}`,
48
- 'Size & spacing': `<ui-text variant="caption">Shared size ladder: buttons, fields and swatches align at each step. Touch raises the family together.</ui-text><ui-stack>${['sm','md','lg'].map((size,index) => `<ui-stack gap="xs" data-size-sample="${size}"><ui-text variant="caption">${['Small','Medium','Large'][index]}<span data-size-readout></span></ui-text><ui-cluster><ui-button size="${size}">Action</ui-button><ui-input size="${size}" style="--ui-input-inline-size: 10ch" label="${size} size sample" value="Frodo"></ui-input><ui-color-picker size="${size}" label="${size} swatch sample"></ui-color-picker></ui-cluster></ui-stack>`).join('')}</ui-stack><div class="spacing-samples" data-spacing-samples role="img" aria-label="Spacing progression"><span></span><span></span><span></span><span></span></div><ui-disclosure summary="How the scale grows"><ui-stack><p>Control roominess relates every field and button to base text. Size ratio separates small from large. On touch, the whole family grows together to keep targets comfortable. Spacing and its curve shape page rhythm independently.</p>${themeFields.filter(item => ['scale-contrast','space-curve'].includes(item.id)).map(renderField).join('')}</ui-stack></ui-disclosure>`,
48
+ 'Size & spacing': `<ui-text variant="caption">Shared size ladder: buttons, fields and swatches align at each step.</ui-text><ui-stack>${['sm','md','lg'].map((size,index) => `<ui-stack gap="xs" data-size-sample="${size}"><ui-text variant="caption">${['Small','Medium','Large'][index]}<span data-size-readout></span></ui-text><ui-cluster><ui-button size="${size}">Action</ui-button><ui-input size="${size}" style="--ui-input-inline-size: 10ch" label="${size} size sample" value="Frodo"></ui-input><ui-color-picker size="${size}" label="${size} swatch sample"></ui-color-picker></ui-cluster></ui-stack>`).join('')}</ui-stack><div class="spacing-samples" data-spacing-samples role="img" aria-label="Spacing progression"><span></span><span></span><span></span><span></span></div><ui-disclosure summary="How the scale grows"><ui-stack><p>Control roominess relates every field and button to base text. Size ratio separates small from large. Visible proportions stay the same on touch; hit areas are a separate design choice. Spacing and its curve shape page rhythm independently.</p>${themeFields.filter(item => ['scale-contrast','space-curve'].includes(item.id)).map(renderField).join('')}</ui-stack></ui-disclosure>`,
49
49
  };
50
50
  const families = [...new Set(themeFields.map(field => field.family))].filter(family => !['Colour', 'Material'].includes(family)).map(family => `<ui-disclosure summary="${family}"><ui-stack>${themeFields.filter(item => item.family === family && !['scale-contrast', 'space-curve'].includes(item.id)).map(renderField).join('')}${supplements[family] ?? ''}</ui-stack></ui-disclosure>`).join('');
51
51
  const refinement = (family, content) => `<section data-refinement-view="${family}" hidden tabindex="-1" aria-label="${family === 'material' ? 'Material and light refinement' : 'Palette refinement'}"><ui-stack gap="xl"><ui-cluster justify="between"><ui-button data-action="back">Back to customization</ui-button><ui-button data-action="restore-${family}">Restore ${family} preset</ui-button></ui-cluster><ui-text variant="heading" as="h2">${family === 'material' ? 'Refine material & light' : 'Refine your palette'}</ui-text><p>These are advanced refinements of the same persistent theme. Every change applies throughout your documentation.</p>${renderPreset(family, 'refinement')}${content}</ui-stack></section>`;
@@ -59,7 +59,7 @@ export function customizerResource(site, input = defaultRecipe()) {
59
59
  <div data-looma-customizer data-recipe="${escape(JSON.stringify(recipe))}" data-base="${escape(site.base)}" data-mode="${site.config.local ? 'local' : 'public'}" data-revision="${escape(site.config.revision ?? '')}">
60
60
  <ui-stack gap="l"><ui-text>Choose a starting point, refine shared decisions, and see them on real documentation examples.</ui-text>
61
61
  <ui-text variant="caption" role="status" aria-live="polite" data-message>Changes are remembered across the documentation.</ui-text>
62
- <ui-cluster><ui-text variant="label" data-theme-origin></ui-text><ui-button size="sm" data-action="undo" disabled>Undo</ui-button><ui-button size="sm" data-action="redo" disabled>Redo</ui-button><ui-button size="sm" data-action="compare" aria-pressed="false">Compare entry theme</ui-button></ui-cluster>
62
+ <ui-cluster><ui-text variant="label" data-theme-origin></ui-text><ui-button size="sm" data-action="undo" disabled>Undo</ui-button><ui-button size="sm" data-action="redo" disabled>Redo</ui-button></ui-cluster>
63
63
  <ui-disclosure summary="Explore nearby styles"><ui-stack><ui-text variant="caption">Preview a small change before applying it. Locks preserve your selected families.</ui-text><ui-cluster>${[['palette','Palette'],['material','Material'],['type','Type & spacing']].map(([id,label]) => `<ui-checkbox size="sm" data-family-lock="${id}">Lock ${label}</ui-checkbox>`).join('')}</ui-cluster><ui-form-field><label slot="label" for="nearby-style">Nearby style</label><ui-select id="nearby-style" data-variation-choice>${Object.entries(themeVariations).map(([id,label]) => `<option value="${id}">${label}</option>`).join('')}</ui-select></ui-form-field><ui-button data-action="preview-variation">Preview variation</ui-button><div data-variation-confirm hidden><ui-stack><ui-text data-variation-description role="status"></ui-text><ui-cluster><ui-button preset="primary" data-action="apply-variation">Apply variation</ui-button><ui-button data-action="cancel-variation">Cancel preview</ui-button></ui-cluster></ui-stack></div></ui-stack></ui-disclosure>
64
64
  ${materialView}${paletteView}
65
65
  <div class="customizer-layout" data-customizer-overview>
@@ -73,6 +73,7 @@ export function customizerResource(site, input = defaultRecipe()) {
73
73
  <ui-disclosure summary="Use your theme"><p>Every export includes light and dark. Leave data-theme off the page to follow system preference, or set it to light or dark on the page or a section. <a href="${escape(site.base)}design-system/">Installation and mode guidance</a></p></ui-disclosure>
74
74
  <ui-disclosure summary="Theme CSS"><ui-textarea data-css readonly rows="12" aria-label="Generated theme CSS" value="${escape(themeCSS(recipe))}"></ui-textarea></ui-disclosure></ui-stack></ui-disclosure>
75
75
  </ui-stack></aside>
76
+ <section data-theme-sampler><ui-stack gap="xs"><ui-text variant="heading" as="h2">Theme sampler</ui-text>${documentationCard('ui-cluster', '/99-theme-sampler', true)}</ui-stack></section>
76
77
  <section class="examples"><ui-stack><ui-form-field><label slot="label" for="example-query">Filter previews</label><ui-input id="example-query" type="search" data-example-query placeholder="Name, tag or purpose"></ui-input></ui-form-field><ui-text variant="caption" role="status" aria-live="polite" data-example-count></ui-text><ui-disclosure summary="Add an example"><ui-stack><ui-combobox id="example-choice" data-add-example label="Choose a component example" placeholder="Search buttons, fields, dialogs…" disclosure clearable>${options}</ui-combobox><ui-button data-action="add" disabled>Add to preview</ui-button></ui-stack></ui-disclosure>
77
78
  <ui-grid columns="2" min="sm"><section $each="card of $cards" $key="$card.id"><looma-example-card ${cardFields.filter(name => name !== 'id').map(name => `from:${name.replace(/[A-Z]/g, letter => '-' + letter.toLowerCase())}="$card.${name}"`).join(' ')} from:identity="$card.id" loading="eager" removable></looma-example-card></section></ui-grid>
78
79
  <noscript>Enable JavaScript to edit the theme. The component reference and example previews remain available.</noscript>
@@ -80,7 +81,7 @@ export function customizerResource(site, input = defaultRecipe()) {
80
81
  <style>
81
82
  [data-customizer-overview][hidden], [data-refinement-view][hidden], [data-variation-confirm][hidden] { display: none !important; }
82
83
  .customizer-layout { display: grid !important; grid-template-columns: minmax(16rem, 20rem) minmax(0, 1fr); gap: var(--ui-space-6) !important; align-items: start !important; }
83
- aside { position: sticky; inset-block-start: 5rem; max-block-size: calc(100dvh - 6rem); overflow: auto; padding-inline-end: var(--ui-space-2) !important; }
84
+ aside { grid-row: 1 / span 2; position: sticky; inset-block-start: 5rem; max-block-size: calc(100dvh - 6rem); overflow: auto; padding: max(var(--ui-space-2), calc(var(--ui-focus-width) * 2)) !important; scroll-padding: max(var(--ui-space-2), calc(var(--ui-focus-width) * 2)); }
84
85
  .examples { min-inline-size: 0; }
85
86
  .refinement-layout { display: grid !important; grid-template-columns: minmax(0, 1.2fr) minmax(16rem, 1fr); gap: var(--ui-space-6) !important; align-items: start !important; }
86
87
  [data-refinement-view] { scroll-margin-block-start: 5rem; }
@@ -94,6 +95,6 @@ export function customizerResource(site, input = defaultRecipe()) {
94
95
  .palette-canvas { background: var(--ui-surface); }
95
96
  .palette-content { background: var(--ui-surface-raised); }
96
97
  .palette-action { background: var(--ui-accent); }
97
- @media (max-width: 64rem) { .customizer-layout, .refinement-layout { grid-template-columns: minmax(0, 1fr); } aside { position: static; max-block-size: none; } }
98
+ @media (max-width: 64rem) { .customizer-layout, .refinement-layout { grid-template-columns: minmax(0, 1fr); } aside { grid-row: auto; order: 2; position: static; max-block-size: none; } [data-theme-sampler] { order: 1; } .examples { order: 3; } }
98
99
  </style></template>`;
99
100
  }