@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
@@ -22,8 +22,7 @@ export function connect(host) {
22
22
  let alive = true;
23
23
  let selectedExample = '';
24
24
  let refinementTrigger;
25
- let previewFrame = 0, pending = false, comparing = false, candidate, gestureStart;
26
- const entryRecipe = structuredClone(recipe);
25
+ let previewFrame = 0, pending = false, candidate, gestureStart;
27
26
  const abort = new AbortController();
28
27
  const say = (text, error = false) => { message.textContent = text; message.setAttribute('role', error ? 'alert' : 'status'); };
29
28
  const sayApplied = text => say(theme?.storageAvailable ? text : 'Theme applies to this page. Browser storage is unavailable; download CSS or a recipe to keep it.');
@@ -106,16 +105,15 @@ export function connect(host) {
106
105
  changed = JSON.stringify(recipe) !== JSON.stringify(saved);
107
106
  paint();
108
107
  };
109
- const clearComparison = () => {
110
- if (!comparing && !candidate) return;
111
- comparing = false; candidate = undefined; theme?.compare();
112
- root.querySelector('[data-action="compare"]').setAttribute('aria-pressed', 'false');
108
+ const clearPreview = () => {
109
+ if (!candidate) return;
110
+ candidate = undefined; theme?.compare();
113
111
  root.querySelector('[data-variation-confirm]').hidden = true;
114
112
  };
115
113
  const preview = next => {
116
114
  const validated = validateRecipe(next);
117
115
  if (JSON.stringify(validated.values) === JSON.stringify(recipe.values)) return;
118
- clearComparison();
116
+ clearPreview();
119
117
  if (!pending) gestureStart = structuredClone(recipe);
120
118
  recipe = validated; pending = true;
121
119
  if (!previewFrame) previewFrame = view.requestAnimationFrame(() => {
@@ -189,17 +187,13 @@ export function connect(host) {
189
187
  if (!button) { view.requestAnimationFrame(paint); return; }
190
188
  if (button.dataset.action === 'save' && saving) return;
191
189
  flush();
192
- if (!['compare', 'preview-variation', 'apply-variation', 'cancel-variation'].includes(button.dataset.action)) clearComparison();
190
+ if (!['preview-variation', 'apply-variation', 'cancel-variation'].includes(button.dataset.action)) clearPreview();
193
191
  try {
194
192
  switch (button.dataset.action) {
195
193
  case 'undo': case 'redo':
196
194
  theme?.[button.dataset.action](); show(theme?.recipe ?? recipe, false); sayApplied('Previous theme decision restored.'); break;
197
- case 'compare':
198
- candidate = undefined; comparing = !comparing; theme?.compare(comparing ? entryRecipe : undefined);
199
- button.setAttribute('aria-pressed', String(comparing)); root.querySelector('[data-variation-confirm]').hidden = true;
200
- say(comparing ? 'Comparing with the theme when you entered. Your edits remain intact. Choose Compare again to return.' : 'Back to your edited theme.'); break;
201
195
  case 'preview-variation': {
202
- clearComparison();
196
+ clearPreview();
203
197
  const locks = [...root.querySelectorAll('[data-family-lock]')].filter(control => control.querySelector('input')?.checked).map(control => control.dataset.familyLock);
204
198
  candidate = nearbyVariation(recipe, root.querySelector('[data-variation-choice]').value, locks);
205
199
  theme?.compare(candidate); root.querySelector('[data-variation-confirm]').hidden = false;
@@ -208,10 +202,10 @@ export function connect(host) {
208
202
  break;
209
203
  }
210
204
  case 'apply-variation': {
211
- const selected = candidate; clearComparison();
205
+ const selected = candidate; clearPreview();
212
206
  if (selected) show(selected); sayApplied('Variation applied. Undo returns to your previous theme.'); break;
213
207
  }
214
- case 'cancel-variation': clearComparison(); paint(); say('Variation cancelled; your theme is unchanged.'); break;
208
+ case 'cancel-variation': clearPreview(); paint(); say('Variation cancelled; your theme is unchanged.'); break;
215
209
  case 'light-top': case 'light-left': case 'light-center': {
216
210
  const positions = { 'light-top': [.5, 0], 'light-left': [.15, .15], 'light-center': [.5, .5] };
217
211
  const [x, y] = positions[button.dataset.action];
@@ -275,7 +269,7 @@ export function connect(host) {
275
269
  if (event.target.matches('[data-light-editor]') || event.target.closest?.('[data-token]')) {
276
270
  input(event); flush(); sayApplied('Theme remembered in this browser. Undo restores the previous decision.'); return;
277
271
  }
278
- flush(); clearComparison();
272
+ flush(); clearPreview();
279
273
  if (event.target.matches('[data-unit-for]')) {
280
274
  const field = fields.get(event.target.dataset.unitFor);
281
275
  try {
@@ -303,7 +297,7 @@ export function connect(host) {
303
297
  const cancel = event => { if (event.target.matches('[data-light-editor], [data-editor-kind="curve"]')) { flush(false); say('Drag cancelled; the previous value is restored.'); } };
304
298
  const keyboard = event => {
305
299
  if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== 'z' || event.target.closest?.('input,textarea,select,[contenteditable="true"]')) return;
306
- event.preventDefault(); flush(); clearComparison(); theme?.[event.shiftKey ? 'redo' : 'undo'](); show(theme?.recipe ?? recipe, false);
300
+ event.preventDefault(); flush(); clearPreview(); theme?.[event.shiftKey ? 'redo' : 'undo'](); show(theme?.recipe ?? recipe, false);
307
301
  };
308
302
  const ready = event => {
309
303
  if (event.data?.type === 'looma-preview-ready' && [...root.querySelectorAll('iframe')].some(frame => frame.contentWindow === event.source)) paint();
@@ -312,7 +306,7 @@ export function connect(host) {
312
306
  let fontRequest = 0;
313
307
  const selectFont = async event => {
314
308
  const control = event.target.closest?.('[data-font-picker]'); if (!control) return;
315
- flush(); clearComparison();
309
+ flush(); clearPreview();
316
310
  const id = control.dataset.fontPicker, family = event.detail.value;
317
311
  const fonts = { ...recipe.fonts };
318
312
  if (family === 'system') delete fonts[id]; else fonts[id] = family;
@@ -358,5 +352,5 @@ export function connect(host) {
358
352
  root.querySelector('[data-action="save"]').hidden = false;
359
353
  }).catch(error => { if (alive && error.name !== 'AbortError') say(error.message, true); });
360
354
  }
361
- return () => { root.removeEventListener('toggle', updateSizes, true); flush(); clearComparison(); root.removeEventListener('cancel', cancel); root.removeEventListener('keydown', keyboard); alive = false; abort.abort(); root.removeEventListener('looma-example-remove', remove); root.removeEventListener('input', input); root.removeEventListener('value-change', selectExample); root.removeEventListener('value-change', selectFont); root.removeEventListener('blur', blur, true); root.removeEventListener('change', change); root.removeEventListener('click', click); view.removeEventListener('beforeunload', flush); view.removeEventListener('message', ready); view.removeEventListener(themeEvent, synchronize); };
355
+ return () => { root.removeEventListener('toggle', updateSizes, true); flush(); clearPreview(); root.removeEventListener('cancel', cancel); root.removeEventListener('keydown', keyboard); alive = false; abort.abort(); root.removeEventListener('looma-example-remove', remove); root.removeEventListener('input', input); root.removeEventListener('value-change', selectExample); root.removeEventListener('value-change', selectFont); root.removeEventListener('blur', blur, true); root.removeEventListener('change', change); root.removeEventListener('click', click); view.removeEventListener('beforeunload', flush); view.removeEventListener('message', ready); view.removeEventListener(themeEvent, synchronize); };
362
356
  }
package/docs/theme.mjs CHANGED
@@ -29,12 +29,12 @@ export const themeFields = [
29
29
  ['Size & spacing', 'line-weight', 'Line weight', '--ui-border-width', 'length', '1px', 'Shared edge weight; focus stays independently visible.'],
30
30
  ['Material', 'outer-depth', 'Outer depth', '--ui-outer-depth', 'nonnegative', '0.6', 'How strongly controls and floating surfaces rise above the page.'],
31
31
  ['Material', 'inner-depth', 'Inner depth', '--ui-inner-depth', 'nonnegative', '0.3', 'How strongly fields and pressed controls sit into their surface.'],
32
- ['Material', 'glassiness', 'Glassiness', '--ui-glassiness', 'fraction', '0', 'Frosted blur, richer background colour and light-catching glass edges. Content stays readable.'],
32
+ ['Material', 'glassiness', 'Glassiness', '--ui-glassiness', 'fraction', '0', 'Clear through richly frosted: the upper range adds stronger blur, saturation and glass edges. Content stays readable.'],
33
33
  ['Material', 'surface-separation', 'Surface separation', '--ui-surface-separation', 'fraction', '0', 'One material offset, scaled by popup purpose and actual size: quiet tooltips through substantial dialogs.'],
34
34
  ['Material', 'translucency', 'Overlay translucency', '--ui-overlay-translucency', 'fraction', '0', 'How much background comes through floating surfaces.'],
35
35
  ['Material', 'edge-light', 'Edge light', '--ui-edge-light', 'fraction', '0', 'Light caught by surface edges, independent of depth.'],
36
36
  ['Material', 'finish', 'Finish', '--ui-finish', 'fraction', '0', 'Matte through polished: how strongly the material catches a reflection.'],
37
- ['Material', 'glow', 'Glow', '--ui-glow', 'fraction', '0', 'Soft reflected light beyond a surface, derived from its tone.'],
37
+ ['Material', 'glow', 'Glow', '--ui-glow', 'fraction', '0', 'Reflected light beyond a surface, from a quiet halo to a vivid bloom at the upper end.'],
38
38
  ['Material', 'light-x', 'Light horizontal position', '--ui-light-x', 'fraction', '0.5', 'The light source from left to right, relative to each surface.'],
39
39
  ['Material', 'light-y', 'Light vertical position', '--ui-light-y', 'fraction', '0', 'The light source from top to bottom, relative to each surface.'],
40
40
  ['Material', 'light-spread', 'Light spread', '--ui-light-spread', 'fraction', '0.5', 'A concentrated spot through broad illumination.'],
@@ -187,7 +187,7 @@ const contrast = (a, b) => (Math.max(luminance(a), luminance(b)) + .05) / (Math.
187
187
  // fill cannot wash out the label, even at maximum finish and light strength.
188
188
  function reflection(fill, text) {
189
189
  const labels = Array.isArray(text) ? text : [text];
190
- let amount = .32;
190
+ let amount = .6;
191
191
  while (amount > 0 && labels.some(label => contrast(mix('#ffffff', fill, amount), label) < 4.5)) amount = Math.max(0, amount - .005);
192
192
  return mix('#ffffff', fill, amount);
193
193
  }
package/editor/icons.d.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  import { type IconNode } from 'lucide';
2
2
  /** The opinionated Lucide set used by Looma's shipped interaction surfaces. */
3
3
  export declare const LOOMA_ICONS: {
4
+ monitor: IconNode;
5
+ sun: IconNode;
6
+ moon: IconNode;
4
7
  'align-center': IconNode;
5
8
  'align-left': IconNode;
6
9
  'align-right': IconNode;
package/editor/index.js CHANGED
@@ -2,10 +2,13 @@ import { n as TABLE_CELL_BACKGROUND_PRESETS, t as TABLE_CELL_BACKGROUND_OPTIONS
2
2
  import { n as resolveTableCellAt, t as measureTableOverlayGeometry } from "./chunks/ui.js";
3
3
  import { $ as LOOMA_ACTIVE_BLOCK_BLUR_GRACE_MS, A as filterLoomaMentionItems, B as LOOMA_FOOTNOTE_LABEL, C as loomaMarkdownToHtml, D as createLoomaMentionExtension, E as LoomaMentionSuggestionPluginKey, F as LoomaTableOfContents, G as LoomaFigcaption, H as LoomaFootnoteReference, I as getTableOfContentsEntries, J as revealLoomaExpandAt, K as LoomaFigure, L as normalizeTableOfContentsConfig, M as LoomaImage, N as normalizeImageDimension, O as DEFAULT_MENTION_RESULT_LIMIT, P as normalizeImagePlacement, Q as LoomaCallout, R as LoomaTab, S as loomaMarkdownFromDoc, T as siteRelativeHref, U as LoomaFootnotes, V as LoomaFootnote, W as findLoomaFootnote, X as LoomaChip, Y as LOOMA_CHIP_COLORS, Z as normalizeLoomaChipColor, _ as getActiveTableCellAlignment, a as getActiveTableUiState, b as setActiveTableCellBackground, c as insertTableAtRange, d as LoomaTableKit, et as LOOMA_ACTIVE_BLOCK_CLASS, f as getDefaultEditorExtensions, g as LoomaTableHeader, h as LoomaTableCell, i as getDefaultSlashCommands, j as normalizeMentionResultLimit, k as MAX_MENTION_RESULT_LIMIT, l as normalizeActiveTableColumnWidths, m as LoomaTable, n as createLoomaSlashCommandExtension, nt as LoomaActiveBlock, o as handleTableAction, p as getLoomaTableExtensions, q as LoomaExpand, r as filterLoomaSlashCommands, rt as LoomaActiveBlockPluginKey, s as handleTableOverlayAction, t as LoomaSlashCommand, tt as LOOMA_ACTIVE_BLOCK_TYPING_IDLE_MS, u as shouldShowTextFormattingToolbar, v as getActiveTableCellBackground, w as createSiteRelativeLinks, x as LoomaSmartPaste, y as setActiveTableCellAlignment, z as LoomaTabs } from "./chunks/extensions.js";
4
4
  import { a as setReferenceDisplay, i as referenceDisplay, n as LoomaReferencePresentation, o as LOOMA_CALLOUT_TONES, r as getReferenceDisplay, t as LoomaReference } from "./chunks/reference-display.js";
5
- import { AlignCenter, AlignLeft, AlignRight, ArrowLeft, Bell, Bold, BookUser, Braces, Calculator, Check, ChevronDown, ChevronLeft, ChevronRight, CircleCheck, CircleDot, CircleQuestionMark, CircleX, CodeXml, Columns3, Compass, CreditCard, Ellipsis, Eraser, ExternalLink, FileText, Folder, GripHorizontal, GripVertical, Heading1, Heading2, Heading3, Highlighter, History, Image, Info, Italic, LayoutDashboard, Lightbulb, Link, List, ListOrdered, ListTodo, LoaderCircle, Merge, MessageSquareWarning, Minus, NotebookPen, PaintBucket, PanelBottom, PanelLeft, PanelRight, PanelTop, Paperclip, Pencil, Pilcrow, Plus, Quote, Receipt, Redo2, Rows3, Search, Settings, Split, Strikethrough, Superscript, Table2, Tag, Trash2, TriangleAlert, Truck, Underline, Undo2, Unlink, UserRound, Users, X } from "lucide";
5
+ import { AlignCenter, AlignLeft, AlignRight, ArrowLeft, Bell, Bold, BookUser, Braces, Calculator, Check, ChevronDown, ChevronLeft, ChevronRight, CircleCheck, CircleDot, CircleQuestionMark, CircleX, CodeXml, Columns3, Compass, CreditCard, Ellipsis, Eraser, ExternalLink, FileText, Folder, GripHorizontal, GripVertical, Heading1, Heading2, Heading3, Highlighter, History, Image, Info, Italic, LayoutDashboard, Lightbulb, Link, List, ListOrdered, ListTodo, LoaderCircle, Merge, MessageSquareWarning, Minus, Monitor, Moon, NotebookPen, PaintBucket, PanelBottom, PanelLeft, PanelRight, PanelTop, Paperclip, Pencil, Pilcrow, Plus, Quote, Receipt, Redo2, Rows3, Search, Settings, Split, Strikethrough, Sun, Superscript, Table2, Tag, Trash2, TriangleAlert, Truck, Underline, Undo2, Unlink, UserRound, Users, X } from "lucide";
6
6
  //#region src/editor/icons.ts
7
7
  /** The opinionated Lucide set used by Looma's shipped interaction surfaces. */
8
8
  var LOOMA_ICONS = {
9
+ monitor: Monitor,
10
+ sun: Sun,
11
+ moon: Moon,
9
12
  "align-center": AlignCenter,
10
13
  "align-left": AlignLeft,
11
14
  "align-right": AlignRight,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threadlabs/looma",
3
- "version": "0.37.0",
3
+ "version": "0.37.1",
4
4
  "type": "module",
5
5
  "description": "Looma components as HTML, Vue, and plain DOM, with design tokens.",
6
6
  "license": "MIT",
@@ -127,7 +127,7 @@
127
127
  "@tiptap/pm": ">=2 <3",
128
128
  "@tiptap/vue-3": ">=2 <3",
129
129
  "vue": "^3.5.43",
130
- "markupress": "^0.8.7",
130
+ "markupress": "^0.8.7 || ^0.9.0",
131
131
  "@nextwebwg/htmlkit": "1.0.0-alpha.69",
132
132
  "vite": "^8.3.4"
133
133
  },
@@ -451,18 +451,9 @@
451
451
  }
452
452
 
453
453
  /* The size states outrank :host, so sm is named too, or a small button would stay small. */
454
- @media (pointer: coarse) {
455
- :scope:where([data-component~="ui-button"]) {
456
- min-block-size: max(var(--ui-control-size-md), var(--ui-control-min-block-size));
457
- }
458
- :scope:where([data-component~="ui-button"])[data-ui-button-state~="size=sm"] {
459
- min-block-size: max(var(--ui-control-size-sm), var(--ui-control-min-block-size));
460
- }
461
- }
462
454
 
463
455
  /* Under touch, a link-style button's press lands within the control minimum: an invisible hit
464
- area, centred on it, like IconButton's. A boxed button grows to the minimum itself (below);
465
- a link-style one keeps its text size, so a standalone one ("See all" under a list) needs the
456
+ area, centred on it, like IconButton's. A link-style action keeps its text size, so a standalone one ("See all" under a list) needs the
466
457
  area. xs sits inside a line of text the same way, so it keeps its size and gains the area too.
467
458
  Only these need it: a hit area on every button would reach over its neighbours. */
468
459
  html[data-ui-input-modality="touch"] :scope:where([data-component~="ui-button"])[data-appearance="link"],
@@ -567,12 +558,9 @@
567
558
  /* Ghost keeps no border, but gains the same neutral surface as other disabled buttons. */
568
559
 
569
560
  /* The size states outrank :host, so sm is named too, or a small button would stay small. */
570
- @media (pointer: coarse) {
571
- }
572
561
 
573
562
  /* Under touch, a link-style button's press lands within the control minimum: an invisible hit
574
- area, centred on it, like IconButton's. A boxed button grows to the minimum itself (below);
575
- a link-style one keeps its text size, so a standalone one ("See all" under a list) needs the
563
+ area, centred on it, like IconButton's. A link-style action keeps its text size, so a standalone one ("See all" under a list) needs the
576
564
  area. xs sits inside a line of text the same way, so it keeps its size and gains the area too.
577
565
  Only these need it: a hit area on every button would reach over its neighbours. */
578
566
 
@@ -52,7 +52,7 @@
52
52
  }
53
53
 
54
54
  :scope:where([data-component~="ui-card"])[data-ui-card-state~="treatment=expressive"] {
55
- --_ui-panel-glass: clamp(0, var(--ui-glassiness, 0), 1);
55
+ --_ui-panel-glass: calc(clamp(0, var(--ui-glassiness, 0), 1) + 2 * pow(clamp(0, var(--ui-glassiness, 0), 1), 4));
56
56
  --_ui-panel-separation: clamp(0, var(--ui-surface-separation, 0), 1);
57
57
  --_ui-panel-rim: calc((var(--_ui-panel-separation) + var(--_ui-panel-glass) * 0.25) * 0.65 * 8px);
58
58
  border-width: calc(var(--ui-border-width) + var(--_ui-panel-rim));
@@ -210,12 +210,6 @@
210
210
  padding-block: max(0px, calc((var(--_row) - max(var(--_line), var(--_ui-checkbox-size, 1.125rem))) / 2)) !important;
211
211
  }
212
212
 
213
- @media (pointer: coarse) {
214
- :scope:where([data-component~="ui-checkbox"])[data-ui-checkbox-state~="size=sm"] {
215
- --_row: var(--ui-control-min-block-size);
216
- }
217
- }
218
-
219
213
  /* A component that sets its display still honours the hidden attribute on its root. */
220
214
  :scope:where([data-component~="ui-checkbox"])[hidden] {
221
215
  display: none !important;
@@ -235,8 +229,5 @@
235
229
  and the first line centres in that control height, so the label shares a row's baseline.
236
230
  Multi-line labels and descriptions still grow downward from that first line. */
237
231
 
238
- @media (pointer: coarse) {
239
- }
240
-
241
232
  /* A component that sets its display still honours the hidden attribute on its root. */
242
233
  }
@@ -104,17 +104,4 @@
104
104
  padding: 0 !important;
105
105
  accent-color: var(--ui-accent-solid);
106
106
  }
107
- @media (pointer: coarse) {
108
- :scope:where([data-component~="ui-color-picker"]) {
109
- --_size: max(var(--ui-control-size-md), var(--ui-control-min-block-size));
110
- }
111
- :scope:where([data-component~="ui-color-picker"])[data-ui-color-picker-state~="size=sm"] {
112
- --_size: max(var(--ui-control-size-sm), var(--ui-control-min-block-size));
113
- --_font: var(--ui-font-size-md);
114
- }
115
- }
116
- }
117
- @scope ([data-component~="ui-color-picker"]) to ([data-component]) {
118
- @media (pointer: coarse) {
119
- }
120
107
  }
@@ -310,20 +310,8 @@
310
310
  white-space: nowrap;
311
311
  }
312
312
  @media (pointer: coarse) {
313
- :scope:where([data-component~="ui-combobox"]) {
314
- --_row: max(var(--ui-control-size-md), var(--ui-control-min-block-size));
315
- }
316
- button {
317
- min-inline-size: 2.75rem;
318
- min-block-size: calc(var(--_row) - 2 * var(--ui-border-width));
319
- }
320
- /* Under touch a small field is still a touch-sized target, as a small button is, and keeps
321
- body-size text: below 16px, iOS zooms the page into the focused field. */
322
- :scope:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=sm"] {
323
- --_row: max(var(--ui-control-size-sm), var(--ui-control-min-block-size));
324
- }
325
313
  :scope:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=sm"] input {
326
- font-size: inherit;
314
+ font-size: var(--ui-font-size-md);
327
315
  }
328
316
  }
329
317
  @media (forced-colors: active) {
@@ -408,8 +396,6 @@
408
396
  /* A ghost icon button: the affordance is the mark, not a box around it. */
409
397
  /* A row with a tag or a description: the label and its tag on one line, the description under them. */
410
398
  @media (pointer: coarse) {
411
- /* Under touch a small field is still a touch-sized target, as a small button is, and keeps
412
- body-size text: below 16px, iOS zooms the page into the focused field. */
413
399
  }
414
400
  @media (forced-colors: active) {
415
401
  }
@@ -722,20 +708,8 @@
722
708
  white-space: nowrap;
723
709
  }
724
710
  @media (pointer: coarse) {
725
- :where([data-component~="ui-combobox"]):where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-combobox"]))) {
726
- --_row: max(var(--ui-control-size-md), var(--ui-control-min-block-size));
727
- }
728
- button:where(:scope, :scope *) {
729
- min-inline-size: 2.75rem;
730
- min-block-size: calc(var(--_row) - 2 * var(--ui-border-width));
731
- }
732
- /* Under touch a small field is still a touch-sized target, as a small button is, and keeps
733
- body-size text: below 16px, iOS zooms the page into the focused field. */
734
- :where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=sm"]:where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-combobox"]))) {
735
- --_row: max(var(--ui-control-size-sm), var(--ui-control-min-block-size));
736
- }
737
711
  :where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=sm"] input:where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-combobox"]))) {
738
- font-size: inherit;
712
+ font-size: var(--ui-font-size-md);
739
713
  }
740
714
  }
741
715
  @media (forced-colors: active) {
@@ -0,0 +1,36 @@
1
+ /* Generated by HTML Next 1.0.0-alpha.69. Do not edit. */
2
+ @supports (-moz-appearance: none) {
3
+ :where([data-component], [data-slotted]) { --html-next-scope-owner: boundary; }
4
+ :where([data-slotted]:not([data-component])) { --html-next-scope-owner: projected !important; }
5
+ }
6
+ @supports (-moz-appearance: none) {
7
+ [data-component~="ui-cycle-button"] { --html-next-scope-owner: "ui-cycle-button" !important; }
8
+ }
9
+ @scope ([data-component~="ui-cycle-button"]) to ([data-component], [data-slotted]) {
10
+ :scope:where([data-component~="ui-cycle-button"]) {
11
+ display: inline-flex !important;
12
+ align-items: center !important;
13
+ justify-content: center !important;
14
+ gap: 0 !important;
15
+ margin: 0 !important;
16
+ padding: 0 !important;
17
+ vertical-align: middle;
18
+ }
19
+ :scope:where([data-component~="ui-cycle-button"])[hidden] {
20
+ display: none !important;
21
+ }
22
+ }
23
+ @scope ([data-slotted~="ui-cycle-button"]:not([data-component])) to ([data-component], [data-slotted]) {
24
+ :where([data-component~="ui-cycle-button"]):where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-cycle-button"]))) {
25
+ display: inline-flex !important;
26
+ align-items: center !important;
27
+ justify-content: center !important;
28
+ gap: 0 !important;
29
+ margin: 0 !important;
30
+ padding: 0 !important;
31
+ vertical-align: middle;
32
+ }
33
+ :where([data-component~="ui-cycle-button"])[hidden]:where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-cycle-button"]))) {
34
+ display: none !important;
35
+ }
36
+ }
@@ -74,7 +74,7 @@
74
74
  /* Purpose and measured size shape one shared separation lever. The content plane stays opaque;
75
75
  transparency, saturation and edge light belong to the surrounding decorative material. */
76
76
  :scope:where([data-component~="ui-dialog"]) {
77
- --_ui-material-glassiness: clamp(0, var(--ui-glassiness, 0), 1);
77
+ --_ui-material-glassiness: calc(clamp(0, var(--ui-glassiness, 0), 1) + 2 * pow(clamp(0, var(--ui-glassiness, 0), 1), 4));
78
78
  --_ui-material-separation: clamp(0, var(--ui-surface-separation, 0), 1);
79
79
  --_ui-material-active: clamp(0, calc((var(--_ui-material-glassiness) + var(--_ui-material-separation)) * 100000), 1);
80
80
  --_ui-material-rim: calc((var(--_ui-material-separation) + var(--_ui-material-glassiness) * 0.25) * var(--_ui-material-scale, 1) * 8px);
@@ -395,7 +395,7 @@
395
395
  /* Purpose and measured size shape one shared separation lever. The content plane stays opaque;
396
396
  transparency, saturation and edge light belong to the surrounding decorative material. */
397
397
  :where([data-component~="ui-dialog"]):where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-dialog"]))) {
398
- --_ui-material-glassiness: clamp(0, var(--ui-glassiness, 0), 1);
398
+ --_ui-material-glassiness: calc(clamp(0, var(--ui-glassiness, 0), 1) + 2 * pow(clamp(0, var(--ui-glassiness, 0), 1), 4));
399
399
  --_ui-material-separation: clamp(0, var(--ui-surface-separation, 0), 1);
400
400
  --_ui-material-active: clamp(0, calc((var(--_ui-material-glassiness) + var(--_ui-material-separation)) * 100000), 1);
401
401
  --_ui-material-rim: calc((var(--_ui-material-separation) + var(--_ui-material-glassiness) * 0.25) * var(--_ui-material-scale, 1) * 8px);
@@ -331,14 +331,6 @@
331
331
  --_size: var(--ui-control-size-lg);
332
332
  }
333
333
 
334
- @media (pointer: coarse) {
335
- :scope:where([data-component~="ui-icon-button"])[data-ui-icon-button-state~="matchButton"] {
336
- inline-size: max(var(--ui-icon-button-size, var(--_size)), var(--ui-control-min-block-size));
337
- min-inline-size: max(var(--ui-icon-button-size, var(--_size)), var(--ui-control-min-block-size));
338
- min-block-size: max(var(--ui-icon-button-size, var(--_size)), var(--ui-control-min-block-size));
339
- }
340
- }
341
-
342
334
  :scope:where([data-component~="ui-icon-button"])[data-ui-icon-button-state~="round"] {
343
335
  border-radius: var(--ui-radius-round);
344
336
  }
@@ -348,14 +340,18 @@
348
340
  background-color: var(--ui-icon-button-outline-surface, var(--_icon-button-outline-surface, var(--ui-surface-default)));
349
341
  background-image: var(--ui-surface-highlight, none);
350
342
  color: var(--ui-icon-button-outline-text, var(--_icon-button-text, var(--ui-text-primary)));
351
- box-shadow: var(--ui-action-shadow, var(--_ui-action-shadow)), var(--_tone-glow);
343
+ box-shadow:
344
+ var(--ui-action-shadow, var(--_ui-action-shadow)),
345
+ var(--_tone-glow);
352
346
  }
353
347
 
354
348
  :scope:where([data-component~="ui-icon-button"])[data-appearance="solid"] {
355
349
  background-color: var(--ui-icon-button-solid-surface, var(--_icon-button-solid-surface, var(--ui-accent-solid)));
356
350
  background-image: var(--_tone-highlight);
357
351
  color: var(--ui-icon-button-solid-text, var(--_icon-button-solid-text, var(--ui-text-on-accent)));
358
- box-shadow: var(--ui-icon-button-solid-shadow, var(--ui-action-shadow, var(--_ui-action-shadow))), var(--_tone-glow);
352
+ box-shadow:
353
+ var(--ui-icon-button-solid-shadow, var(--ui-action-shadow, var(--_ui-action-shadow))),
354
+ var(--_tone-glow);
359
355
  }
360
356
 
361
357
  :scope:where([data-component~="ui-icon-button"]):hover:enabled:not([aria-disabled="true"]) {
@@ -509,9 +505,6 @@
509
505
 
510
506
  /* A text button's companion uses control geometry; compact icon buttons keep their own density. */
511
507
 
512
- @media (pointer: coarse) {
513
- }
514
-
515
508
  /* Native toggle state persists after pointer release, including during loading. */
516
509
 
517
510
  @media (prefers-reduced-motion: reduce) {
@@ -89,16 +89,6 @@
89
89
  min-block-size: var(--ui-control-size-lg);
90
90
  }
91
91
 
92
- @media (pointer: coarse) {
93
- :scope:where([data-component~="ui-input-group"]) {
94
- min-block-size: max(var(--ui-control-size-md), var(--ui-control-min-block-size));
95
- }
96
-
97
- :scope:where([data-component~="ui-input-group"]):has(.field [data-ui-input-state~="size=sm"]) {
98
- min-block-size: var(--ui-control-min-block-size);
99
- }
100
- }
101
-
102
92
  :scope:where([data-component~="ui-input-group"]):hover:not(:has(:is(input, textarea):focus, :is(input, textarea):disabled, :is(input, textarea):read-only, [aria-invalid="true"], :is(:user-invalid, [data-user-invalid]))) {
103
93
  border-color: var(--ui-control-border-hover, var(--ui-text-secondary));
104
94
  }
@@ -249,9 +239,6 @@
249
239
 
250
240
  /* The frame takes its input's size, so a small group lines up with small buttons and fields. */
251
241
 
252
- @media (pointer: coarse) {
253
- }
254
-
255
242
  /* The ring shows when the field's input would show its own: when it is focus-visible, which a
256
243
  text input is for a pointer as well as a keyboard. A focused action button draws its own. */
257
244
 
@@ -100,28 +100,13 @@
100
100
  font-size: var(--ui-font-size-md);
101
101
  }
102
102
 
103
- /* Under touch a small field is still a touch-sized target, as a small button is, and keeps
104
- body-size text: below 16px, iOS zooms the page into the focused field. */
103
+ /* Touch may use body-size text in a small native field to avoid focus zoom; this does not impose a larger visible control height. */
105
104
  @media (pointer: coarse) {
106
- :scope:where([data-component~="ui-input"]) {
107
- min-block-size: var(
108
- --ui-input-min-block-size,
109
- var(--_ui-default-input-min-block-size, max(var(--ui-control-size-md), var(--ui-control-min-block-size)))
110
- );
111
- }
112
-
113
105
  :scope:where([data-component~="ui-input"]),
114
106
  :scope:where([data-component~="ui-input"])[data-ui-input-state~="size=sm"],
115
107
  :scope:where([data-component~="ui-input"])[data-ui-input-state~="size=lg"] {
116
108
  font-size: var(--ui-font-size-md);
117
109
  }
118
-
119
- :scope:where([data-component~="ui-input"])[data-ui-input-state~="size=sm"] {
120
- min-block-size: var(
121
- --ui-input-min-block-size,
122
- var(--_ui-default-input-min-block-size, max(var(--ui-control-size-sm), var(--ui-control-min-block-size)))
123
- );
124
- }
125
110
  }
126
111
 
127
112
  /* Tabbing into a field selects its text; tint that selection softly instead of the page highlight. */
@@ -195,8 +180,7 @@
195
180
 
196
181
  /* Each size reads the hook and then an Input Group's default first, so a group, which draws the frame, can zero it. */
197
182
 
198
- /* Under touch a small field is still a touch-sized target, as a small button is, and keeps
199
- body-size text: below 16px, iOS zooms the page into the focused field. */
183
+ /* Touch may use body-size text in a small native field to avoid focus zoom; this does not impose a larger visible control height. */
200
184
  @media (pointer: coarse) {
201
185
  }
202
186
 
@@ -103,7 +103,7 @@
103
103
  /* Purpose and measured size shape one shared separation lever. The content plane stays opaque;
104
104
  transparency, saturation and edge light belong to the surrounding decorative material. */
105
105
  :scope:where([data-component~="ui-menu"]):not([inline]) .surface {
106
- --_ui-material-glassiness: clamp(0, var(--ui-glassiness, 0), 1);
106
+ --_ui-material-glassiness: calc(clamp(0, var(--ui-glassiness, 0), 1) + 2 * pow(clamp(0, var(--ui-glassiness, 0), 1), 4));
107
107
  --_ui-material-separation: clamp(0, var(--ui-surface-separation, 0), 1);
108
108
  --_ui-material-active: clamp(0, calc((var(--_ui-material-glassiness) + var(--_ui-material-separation)) * 100000), 1);
109
109
  --_ui-material-rim: calc((var(--_ui-material-separation) + var(--_ui-material-glassiness) * 0.25) * var(--_ui-material-scale, 0.28) * 8px);
@@ -74,7 +74,7 @@
74
74
  /* Purpose and measured size shape one shared separation lever. The content plane stays opaque;
75
75
  transparency, saturation and edge light belong to the surrounding decorative material. */
76
76
  .surface {
77
- --_ui-material-glassiness: clamp(0, var(--ui-glassiness, 0), 1);
77
+ --_ui-material-glassiness: calc(clamp(0, var(--ui-glassiness, 0), 1) + 2 * pow(clamp(0, var(--ui-glassiness, 0), 1), 4));
78
78
  --_ui-material-separation: clamp(0, var(--ui-surface-separation, 0), 1);
79
79
  --_ui-material-active: clamp(0, calc((var(--_ui-material-glassiness) + var(--_ui-material-separation)) * 100000), 1);
80
80
  --_ui-material-rim: calc((var(--_ui-material-separation) + var(--_ui-material-glassiness) * 0.25) * var(--_ui-material-scale, 0.55) * 8px);
@@ -110,12 +110,6 @@
110
110
  padding-block: max(0px, calc((var(--_row) - max(var(--_line), var(--_ui-radio-size, 1.125rem))) / 2)) !important;
111
111
  }
112
112
 
113
- @media (pointer: coarse) {
114
- :scope:where([data-component~="ui-radio"])[data-ui-radio-state~="size=sm"] {
115
- --_row: var(--ui-control-min-block-size);
116
- }
117
- }
118
-
119
113
  /* A component that sets its display still honours the hidden attribute on its root. */
120
114
  :scope:where([data-component~="ui-radio"])[hidden] {
121
115
  display: none !important;
@@ -130,8 +124,5 @@
130
124
  and the first line centres in that control height, so the label shares a row's baseline.
131
125
  Multi-line labels and descriptions still grow downward from that first line. */
132
126
 
133
- @media (pointer: coarse) {
134
- }
135
-
136
127
  /* A component that sets its display still honours the hidden attribute on its root. */
137
128
  }
@@ -143,16 +143,10 @@
143
143
  font-size: var(--ui-font-size-md);
144
144
  }
145
145
 
146
- /* Under touch a small field is still a touch-sized target, as a small button is, and keeps
147
- body-size text: below 16px, iOS zooms the page into the focused field. */
146
+ /* Touch may use body-size text in a small native field to avoid focus zoom; this does not impose a larger visible control height. */
148
147
  @media (pointer: coarse) {
149
- :scope:where([data-component~="ui-select"]) {
150
- min-block-size: max(var(--ui-control-size-md), var(--ui-control-min-block-size));
151
- }
152
-
153
148
  :scope:where([data-component~="ui-select"])[data-ui-select-state~="size=sm"] {
154
- min-block-size: max(var(--ui-control-size-sm), var(--ui-control-min-block-size));
155
- font-size: inherit;
149
+ font-size: var(--ui-font-size-md);
156
150
  }
157
151
  }
158
152
 
@@ -169,8 +163,7 @@
169
163
 
170
164
  /* The app's invalid flag, or the browser's own verdict once the user has left an invalid field. */
171
165
 
172
- /* Under touch a small field is still a touch-sized target, as a small button is, and keeps
173
- body-size text: below 16px, iOS zooms the page into the focused field. */
166
+ /* Touch may use body-size text in a small native field to avoid focus zoom; this does not impose a larger visible control height. */
174
167
  @media (pointer: coarse) {
175
168
  }
176
169
 
@@ -166,12 +166,6 @@
166
166
  padding-block: max(0px, calc((var(--_row) - max(var(--_line), var(--_ui-switch-track-height, 1.5rem))) / 2)) !important;
167
167
  }
168
168
 
169
- @media (pointer: coarse) {
170
- :scope:where([data-component~="ui-switch"])[data-ui-switch-state~="size=sm"] {
171
- --_row: var(--ui-control-min-block-size);
172
- }
173
- }
174
-
175
169
  /* A component that sets its display still honours the hidden attribute on its root. */
176
170
  :scope:where([data-component~="ui-switch"])[hidden] {
177
171
  display: none !important;
@@ -189,8 +183,5 @@
189
183
  and the first line centres in that control height, so the label shares a row's baseline.
190
184
  Multi-line labels and descriptions still grow downward from that first line. */
191
185
 
192
- @media (pointer: coarse) {
193
- }
194
-
195
186
  /* A component that sets its display still honours the hidden attribute on its root. */
196
187
  }
@@ -106,7 +106,7 @@
106
106
  /* Purpose and measured size shape one shared separation lever. The content plane stays opaque;
107
107
  transparency, saturation and edge light belong to the surrounding decorative material. */
108
108
  .surface {
109
- --_ui-material-glassiness: clamp(0, var(--ui-glassiness, 0), 1);
109
+ --_ui-material-glassiness: calc(clamp(0, var(--ui-glassiness, 0), 1) + 2 * pow(clamp(0, var(--ui-glassiness, 0), 1), 4));
110
110
  --_ui-material-separation: clamp(0, var(--ui-surface-separation, 0), 1);
111
111
  --_ui-material-active: clamp(0, calc((var(--_ui-material-glassiness) + var(--_ui-material-separation)) * 100000), 1);
112
112
  --_ui-material-rim: calc((var(--_ui-material-separation) + var(--_ui-material-glassiness) * 0.25) * var(--_ui-material-scale, 0.08) * 8px);