@threadlabs/looma 0.33.1 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/components/shared/overlay.js +8 -2
  2. package/components/shared/surface-material.js +21 -0
  3. package/components/ui-button/ui-button.html +63 -47
  4. package/components/ui-card/ui-card.html +44 -2
  5. package/components/ui-curve-editor/curve.js +10 -0
  6. package/components/ui-curve-editor/ui-curve-editor.html +181 -0
  7. package/components/ui-curve-editor/ui-curve-editor.js +51 -0
  8. package/components/ui-dialog/ui-dialog.html +62 -1
  9. package/components/ui-dialog/ui-dialog.js +9 -1
  10. package/components/ui-icon-button/ui-icon-button.html +75 -23
  11. package/components/ui-menu/ui-menu.html +60 -0
  12. package/components/ui-menu/ui-menu.js +1 -1
  13. package/components/ui-number-input/arithmetic.js +47 -0
  14. package/components/ui-number-input/ui-number-input.html +62 -0
  15. package/components/ui-number-input/ui-number-input.js +44 -0
  16. package/components/ui-popover/ui-popover.html +64 -1
  17. package/components/ui-popover/ui-popover.js +1 -1
  18. package/components/ui-slider/ui-slider.html +84 -0
  19. package/components/ui-slider/ui-slider.js +29 -0
  20. package/components/ui-tabs/ui-tabs.html +6 -1
  21. package/components/ui-text/ui-text.html +31 -6
  22. package/components/ui-tooltip/ui-tooltip.html +60 -0
  23. package/components/ui-tooltip/ui-tooltip.js +1 -1
  24. package/dist/index.js +46 -43
  25. package/docs/README.md +34 -4
  26. package/docs/card.html +16 -2
  27. package/docs/card.js +17 -2
  28. package/docs/catalog.json +1154 -144
  29. package/docs/customizer-render.mjs +35 -9
  30. package/docs/customizer.js +83 -20
  31. package/docs/documentation.css +9 -1
  32. package/docs/index.d.ts +4 -2
  33. package/docs/index.mjs +1 -1
  34. package/docs/preview.js +2 -0
  35. package/docs/render.mjs +15 -6
  36. package/docs/site.mjs +50 -9
  37. package/docs/theme-session.mjs +51 -0
  38. package/docs/theme.mjs +134 -15
  39. package/docs/value-editors.mjs +33 -0
  40. package/package.json +2 -2
  41. package/styles/ui-button.css +45 -45
  42. package/styles/ui-card.css +47 -0
  43. package/styles/ui-curve-editor.css +109 -0
  44. package/styles/ui-dialog.css +132 -0
  45. package/styles/ui-icon-button.css +63 -20
  46. package/styles/ui-menu.css +72 -0
  47. package/styles/ui-number-input.css +34 -0
  48. package/styles/ui-popover.css +74 -0
  49. package/styles/ui-slider.css +53 -0
  50. package/styles/ui-tabs.css +6 -1
  51. package/styles/ui-text.css +23 -1
  52. package/styles/ui-tooltip.css +72 -0
  53. package/theme-system.css +26 -10
  54. package/tokens.css +8 -2
  55. package/vanilla/UiButton.d.ts +1 -0
  56. package/vanilla/UiButton.js +195 -130
  57. package/vanilla/UiCard.d.ts +1 -1
  58. package/vanilla/UiCard.js +1 -1
  59. package/vanilla/UiCurveEditor.d.ts +28 -0
  60. package/vanilla/UiCurveEditor.js +134 -0
  61. package/vanilla/UiIconButton.d.ts +2 -1
  62. package/vanilla/UiIconButton.js +58 -31
  63. package/vanilla/UiNumberInput.d.ts +33 -0
  64. package/vanilla/UiNumberInput.js +79 -0
  65. package/vanilla/UiSlider.d.ts +33 -0
  66. package/vanilla/UiSlider.js +125 -0
  67. package/vanilla/UiText.d.ts +2 -2
  68. package/vanilla/UiText.js +45 -11
  69. package/vanilla/index.js +3 -0
  70. package/vue/UiButton.d.ts +6 -0
  71. package/vue/UiButton.vue +155 -56
  72. package/vue/UiCard.d.ts +2 -2
  73. package/vue/UiCard.vue +55 -5
  74. package/vue/UiCurveEditor.d.ts +53 -0
  75. package/vue/UiCurveEditor.js +2 -0
  76. package/vue/UiCurveEditor.vue +427 -0
  77. package/vue/UiDialog.vue +177 -1
  78. package/vue/UiIconButton.d.ts +8 -2
  79. package/vue/UiIconButton.vue +164 -34
  80. package/vue/UiMenu.vue +89 -0
  81. package/vue/UiNumberInput.d.ts +83 -0
  82. package/vue/UiNumberInput.js +2 -0
  83. package/vue/UiNumberInput.vue +370 -0
  84. package/vue/UiPopover.vue +92 -1
  85. package/vue/UiSlider.d.ts +83 -0
  86. package/vue/UiSlider.js +2 -0
  87. package/vue/UiSlider.vue +412 -0
  88. package/vue/UiTabs.vue +6 -1
  89. package/vue/UiText.d.ts +7 -5
  90. package/vue/UiText.vue +104 -22
  91. package/vue/UiTooltip.vue +89 -0
  92. package/vue/chunks/UiActionBar.js +1 -1
  93. package/vue/chunks/UiAffordanceScope.js +2 -2
  94. package/vue/chunks/UiAvatar.js +2 -2
  95. package/vue/chunks/UiAvatarGroup.js +2 -2
  96. package/vue/chunks/UiBadge.js +2 -2
  97. package/vue/chunks/UiBreadcrumbItem.js +2 -2
  98. package/vue/chunks/UiBreadcrumbs.js +2 -2
  99. package/vue/chunks/UiButton.js +94 -11
  100. package/vue/chunks/UiCallout.js +2 -2
  101. package/vue/chunks/UiCard.js +6 -5
  102. package/vue/chunks/UiCheckbox.js +2 -2
  103. package/vue/chunks/UiCluster.js +2 -2
  104. package/vue/chunks/UiColorPicker.js +2 -2
  105. package/vue/chunks/UiCombobox.js +2 -2
  106. package/vue/chunks/UiContainer.js +2 -2
  107. package/vue/chunks/UiContextMenu.js +2 -2
  108. package/vue/chunks/UiCover.js +2 -2
  109. package/vue/chunks/UiCurveEditor.js +413 -0
  110. package/vue/chunks/UiDescriptionItem.js +2 -2
  111. package/vue/chunks/UiDescriptionList.js +2 -2
  112. package/vue/chunks/UiDialog.js +4 -4
  113. package/vue/chunks/UiDisclosure.js +2 -2
  114. package/vue/chunks/UiEditable.js +2 -2
  115. package/vue/chunks/UiEditorBlockActions.js +2 -2
  116. package/vue/chunks/UiEditorChipEditor.js +2 -2
  117. package/vue/chunks/UiEditorCodeLanguage.js +2 -2
  118. package/vue/chunks/UiEditorFootnoteEditor.js +2 -2
  119. package/vue/chunks/UiEditorImageToolbar.js +3 -3
  120. package/vue/chunks/UiEditorInsertTableGrid.js +2 -2
  121. package/vue/chunks/UiEditorLinkCard.js +3 -3
  122. package/vue/chunks/UiEditorLinkForm.js +3 -3
  123. package/vue/chunks/UiEditorMentionMenu.js +2 -2
  124. package/vue/chunks/UiEditorMentionMenuItem.js +2 -2
  125. package/vue/chunks/UiEditorSlashMenu.js +2 -2
  126. package/vue/chunks/UiEditorSlashMenuGroup.js +2 -2
  127. package/vue/chunks/UiEditorSlashMenuItem.js +2 -2
  128. package/vue/chunks/UiEditorSurface.js +2 -2
  129. package/vue/chunks/UiEditorTableContextMenu.js +2 -2
  130. package/vue/chunks/UiEditorTableOverlay.js +2 -2
  131. package/vue/chunks/UiEditorTableToolbar.js +2 -2
  132. package/vue/chunks/UiEditorToolbar.js +2 -2
  133. package/vue/chunks/UiFloatingActionButton.js +2 -2
  134. package/vue/chunks/UiFormField.js +2 -2
  135. package/vue/chunks/UiGrid.js +2 -2
  136. package/vue/chunks/UiIcon.js +2 -2
  137. package/vue/chunks/UiIconButton.js +77 -11
  138. package/vue/chunks/UiImage.js +2 -2
  139. package/vue/chunks/UiInput.js +2 -2
  140. package/vue/chunks/UiInputGroup.js +2 -2
  141. package/vue/chunks/UiList.js +2 -2
  142. package/vue/chunks/UiListItem.js +2 -2
  143. package/vue/chunks/UiListbox.js +2 -2
  144. package/vue/chunks/UiMenu.js +3 -3
  145. package/vue/chunks/UiMenuGroup.js +2 -2
  146. package/vue/chunks/UiMenuItem.js +2 -2
  147. package/vue/chunks/UiMeter.js +2 -2
  148. package/vue/chunks/UiNavItem.js +2 -2
  149. package/vue/chunks/UiNumberInput.js +390 -0
  150. package/vue/chunks/UiPageHeader.js +2 -2
  151. package/vue/chunks/UiPopover.js +4 -4
  152. package/vue/chunks/UiRadio.js +2 -2
  153. package/vue/chunks/UiRadioGroup.js +2 -2
  154. package/vue/chunks/UiReel.js +2 -2
  155. package/vue/chunks/UiScrollArea.js +2 -2
  156. package/vue/chunks/UiSearchResultRow.js +2 -2
  157. package/vue/chunks/UiSearchShell.js +2 -2
  158. package/vue/chunks/UiSection.js +2 -2
  159. package/vue/chunks/UiSelect.js +2 -2
  160. package/vue/chunks/UiSeparator.js +2 -2
  161. package/vue/chunks/UiSidebar.js +2 -2
  162. package/vue/chunks/UiSlider.js +396 -0
  163. package/vue/chunks/UiSpinner.js +2 -2
  164. package/vue/chunks/UiStack.js +2 -2
  165. package/vue/chunks/UiStatusMessage.js +2 -2
  166. package/vue/chunks/UiSwatch.js +2 -2
  167. package/vue/chunks/UiSwitch.js +2 -2
  168. package/vue/chunks/UiSwitcher.js +2 -2
  169. package/vue/chunks/UiTable.js +2 -2
  170. package/vue/chunks/UiTabs.js +3 -3
  171. package/vue/chunks/UiText.js +53 -12
  172. package/vue/chunks/UiTextarea.js +2 -2
  173. package/vue/chunks/UiToast.js +2 -2
  174. package/vue/chunks/UiToastRegion.js +2 -2
  175. package/vue/chunks/UiTooltip.js +3 -3
  176. package/vue/chunks/UiTopBar.js +1 -1
  177. package/vue/chunks/UiTree.js +2 -2
  178. package/vue/chunks/UiTreeItem.js +2 -2
  179. package/vue/chunks/_plugin-vue_export-helper.js +68 -2
  180. package/vue/chunks/props.js +24 -1
  181. package/vue/components.css +2215 -1203
  182. package/vue/editor/index.js +623 -211
  183. package/vue/index.d.ts +3 -0
  184. package/vue/index.js +5 -2
@@ -3,7 +3,8 @@
3
3
  <prop name="bare" type="boolean" default="false"
4
4
  >Delegates the surface to its content, with no padding, border, background, shadow, or overflow clipping.</prop>
5
5
  <prop name="size" type="keyword" values="content, sm, md, lg" default="content"
6
- >Optional size preset. content keeps the existing intrinsic sizing; the other sizes constrain the surface for compact, medium, or large content.</prop>
6
+ >Optional size preset. content keeps the existing intrinsic sizing; the other sizes constrain the surface for compact, medium, or large content. Shared
7
+ surface separation adapts to popover purpose and rendered size.</prop>
7
8
  <prop name="open" type="boolean" default="false">Whether the popover is open initially or when set externally.</prop>
8
9
  <prop name="for" type="string" default="">ID of the control that toggles and anchors the popover.</prop>
9
10
  <prop
@@ -63,6 +64,7 @@
63
64
  }
64
65
 
65
66
  :host {
67
+ --_ui-material-scale: 0.55;
66
68
  --_ui-popover-surface: var(--ui-popover-surface);
67
69
  --_ui-popover-border: var(--ui-popover-border);
68
70
  --_ui-popover-radius: var(--ui-popover-radius);
@@ -95,6 +97,65 @@
95
97
  box-shadow: var(--_ui-popover-shadow, var(--ui-overlay-shadow, var(--ui-elevation-overlay, var(--ui-shadow-lg))));
96
98
  }
97
99
 
100
+ /* Purpose and measured size shape one shared separation lever. The content plane stays opaque;
101
+ transparency, saturation and edge light belong to the surrounding decorative material. */
102
+ .surface {
103
+ --_ui-material-glassiness: clamp(0, var(--ui-glassiness, 0), 1);
104
+ --_ui-material-separation: clamp(0, var(--ui-surface-separation, 0), 1);
105
+ --_ui-material-active: clamp(0, calc((var(--_ui-material-glassiness) + var(--_ui-material-separation)) * 100000), 1);
106
+ --_ui-material-rim: calc((var(--_ui-material-separation) + var(--_ui-material-glassiness) * 0.25) * var(--_ui-material-scale, 0.55) * 8px);
107
+ --_ui-material-content-color: var(--_ui-popover-surface, var(--ui-material-content, var(--ui-overlay-surface, var(--ui-surface-elevated))));
108
+ --_ui-material-rim-color: color-mix(in srgb, var(--ui-material-glass, var(--ui-surface-elevated)) calc(var(--_ui-material-active) * 100%), transparent);
109
+ background:
110
+ linear-gradient(var(--_ui-material-content-color), var(--_ui-material-content-color)) padding-box,
111
+ linear-gradient(var(--_ui-material-rim-color), var(--_ui-material-rim-color)) border-box,
112
+ var(--_ui-material-content-color) padding-box;
113
+ border-width: calc(var(--ui-border-width) + var(--_ui-material-rim));
114
+ border-color: var(
115
+ --_ui-popover-border,
116
+ color-mix(in oklab, var(--ui-overlay-border, var(--ui-border-strong)) calc((1 - var(--_ui-material-active)) * 100%), transparent)
117
+ );
118
+ box-shadow: var(
119
+ --_ui-popover-shadow,
120
+ var(
121
+ --ui-overlay-shadow,
122
+ 0 calc(var(--_ui-material-separation) * var(--_ui-material-scale, 0.55) * 8px) calc(
123
+ var(--_ui-material-separation) * var(--_ui-material-scale, 0.55) * 24px
124
+ ) color-mix(in oklab, var(--ui-text-primary) 15%, transparent),
125
+ inset 0 0 0 calc(var(--_ui-material-active) * var(--ui-border-width)) var(--ui-material-edge, var(--ui-border-strong)),
126
+ var(--ui-elevation-overlay, var(--ui-shadow-lg))
127
+ )
128
+ );
129
+ backdrop-filter: blur(calc(var(--_ui-material-glassiness) * var(--_ui-material-scale, 0.55) * 18px))
130
+ saturate(calc(1 + var(--_ui-material-glassiness) * var(--_ui-material-scale, 0.55) * 0.18));
131
+ -webkit-backdrop-filter: blur(calc(var(--_ui-material-glassiness) * var(--_ui-material-scale, 0.55) * 18px))
132
+ saturate(calc(1 + var(--_ui-material-glassiness) * var(--_ui-material-scale, 0.55) * 0.18));
133
+ }
134
+
135
+ @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
136
+ .surface {
137
+ background: var(--_ui-material-content-color);
138
+ }
139
+ }
140
+
141
+ @media (prefers-reduced-transparency: reduce) {
142
+ .surface {
143
+ background: var(--_ui-material-content-color);
144
+ backdrop-filter: none;
145
+ -webkit-backdrop-filter: none;
146
+ }
147
+ }
148
+
149
+ @media (forced-colors: active) {
150
+ .surface {
151
+ background: Canvas;
152
+ border-color: CanvasText;
153
+ box-shadow: none;
154
+ backdrop-filter: none;
155
+ -webkit-backdrop-filter: none;
156
+ }
157
+ }
158
+
98
159
  /* A toolbar or card can supply its own surface, including an unclipped shadow. */
99
160
  :host([bare]) .surface {
100
161
  overflow: visible;
@@ -102,6 +163,8 @@
102
163
  border: 0;
103
164
  background: transparent;
104
165
  box-shadow: none;
166
+ backdrop-filter: none;
167
+ -webkit-backdrop-filter: none;
105
168
  }
106
169
 
107
170
  :host(:not(:state(internalOpen))) {
@@ -59,7 +59,7 @@ export function connect(host) {
59
59
  anchor = ids.get(nextFor);
60
60
  if (anchor) anchorExpanded = anchor.getAttribute("aria-expanded");
61
61
  anchor?.addEventListener("click", onAnchorClick);
62
- surface = createAnchoredSurface(element, { anchor, placement: nextPlacement, gap: offset });
62
+ surface = createAnchoredSurface(element, { anchor, placement: nextPlacement, gap: offset, materialRole: "popover" });
63
63
  };
64
64
  const apply = () => {
65
65
  const externalOpen = Boolean(host.props.open.value);
@@ -0,0 +1,84 @@
1
+ <link rel="component" href="../ui-number-input/ui-number-input.html">
2
+ <template component="ui-slider" module="./ui-slider.js">
3
+ <defs>
4
+ <state name="current" type="number" from="$value"></state>
5
+ <prop name="value" type="number" default="0">Current numeric value. Native range and exact entry share bounds.</prop>
6
+ <prop name="min" type="number" default="0">Inclusive lower bound for dragging, keyboard steps and exact entry.</prop>
7
+ <prop name="max" type="number" default="100">Inclusive upper bound for dragging, keyboard steps and exact entry.</prop>
8
+ <prop name="step" type="number" default="1">Positive native range and exact entry increment.</prop>
9
+ <prop name="unit" type="string" default="">Unit shown by the exact editor.</prop>
10
+ <prop name="name" type="string" default="">Optional numeric form field name.</prop>
11
+ <prop name="label" type="string" default="Value">Accessible name for the editing control.</prop>
12
+ <prop name="size" type="keyword" values="sm, md, lg" default="md">Shared field and button height.</prop>
13
+ <prop name="disabled" type="boolean" default="false">Prevents typing, stepping and pointer or keyboard changes while keeping the value visible.</prop>
14
+ <event name="input" type="object"><prop name="value" type="number" required></prop></event>
15
+ <event name="change" type="object"><prop name="value" type="number" required></prop></event>
16
+ </defs>
17
+ <span>
18
+ <input
19
+ $ref="range"
20
+ type="range"
21
+ data-ui-field-control
22
+ from:aria-label="$label"
23
+ from:aria-valuetext="concat($current, $unit)"
24
+ from:value="$value"
25
+ from:min="$min"
26
+ from:max="$max"
27
+ from:step="$step"
28
+ from:disabled="$disabled"
29
+ from:name="$name"
30
+ >
31
+ <ui-number-input
32
+ data-slider-exact
33
+ from:value="concat($current, $unit)"
34
+ from:unit="$unit"
35
+ from:min="$min"
36
+ from:max="$max"
37
+ from:step="$step"
38
+ from:size="$size"
39
+ from:disabled="$disabled"
40
+ from:label="$label"
41
+ ></ui-number-input>
42
+ </span>
43
+ <style>
44
+ :host {
45
+ display: grid !important;
46
+ grid-template-columns: minmax(4rem, 1fr) minmax(8rem, 1fr) !important;
47
+ align-items: center !important;
48
+ gap: var(--ui-space-2) !important;
49
+ margin: 0 !important;
50
+ padding: 0 !important;
51
+ min-inline-size: 0;
52
+ }
53
+ input {
54
+ inline-size: 100%;
55
+ min-inline-size: 0;
56
+ min-block-size: var(--ui-control-size-md);
57
+ margin: 0 !important;
58
+ accent-color: var(--ui-accent-solid);
59
+ cursor: pointer;
60
+ }
61
+ :host([size="sm"]) input {
62
+ min-block-size: var(--ui-control-size-sm);
63
+ }
64
+ :host([size="lg"]) input {
65
+ min-block-size: var(--ui-control-size-lg);
66
+ }
67
+ input:focus-visible {
68
+ outline: var(--ui-focus-width) solid var(--ui-control-focus, var(--ui-accent-solid));
69
+ outline-offset: var(--ui-space-1);
70
+ border-radius: var(--ui-radius-sm);
71
+ }
72
+ input:disabled {
73
+ cursor: not-allowed;
74
+ }
75
+ @media (pointer: coarse) {
76
+ input {
77
+ min-block-size: max(var(--ui-control-size-md), var(--ui-control-min-block-size));
78
+ }
79
+ }
80
+ :host[hidden] {
81
+ display: none !important;
82
+ }
83
+ </style>
84
+ </template>
@@ -0,0 +1,29 @@
1
+ import { boundedNumber, calculateValue } from "../ui-number-input/arithmetic.js";
2
+ import { afterFormReset } from "../shared/form-reset.js";
3
+
4
+ export function connect(host) {
5
+ const { range } = host.refs;
6
+ const exact = host.element.querySelector("[data-slider-exact]");
7
+ let external;
8
+ const paint = value => {
9
+ range.value = String(boundedNumber(value, host.props.min.value, host.props.max.value));
10
+ host.state.current = Number(range.value);
11
+ };
12
+ const edit = event => {
13
+ event.stopPropagation();
14
+ if (host.props.disabled.value) return;
15
+ const raw = event.currentTarget === range ? Number(range.value) : calculateValue(event.detail.value, host.props.unit.value || "");
16
+ paint(raw); host.dispatch(event.type, { value: Number(range.value) });
17
+ };
18
+ for (const field of [range, exact]) for (const type of ["input", "change"]) field.addEventListener(type, edit);
19
+ const stop = host.effect(() => {
20
+ const signature = JSON.stringify([host.props.value.value, host.props.min.value, host.props.max.value, host.props.step.value]);
21
+ if (external === signature) return; external = signature;
22
+ range.min = String(Math.min(Number(host.props.min.value), Number(host.props.max.value)));
23
+ range.max = String(Math.max(Number(host.props.min.value), Number(host.props.max.value)));
24
+ range.step = String(Math.abs(Number(host.props.step.value)) || 1);
25
+ range.defaultValue = String(host.props.value.value); paint(Number(host.props.value.value));
26
+ });
27
+ const reset = afterFormReset(range, () => paint(Number(range.defaultValue)));
28
+ return () => { stop(); reset(); for (const field of [range, exact]) for (const type of ["input", "change"]) field.removeEventListener(type, edit); };
29
+ }
@@ -59,6 +59,10 @@
59
59
  syntax: "*";
60
60
  inherits: false;
61
61
  }
62
+ @property --ui-tabs-panel-padding {
63
+ syntax: "*";
64
+ inherits: false;
65
+ }
62
66
 
63
67
  /* Density resolves into the tab row's own values; the public tokens still override them. */
64
68
  :host {
@@ -66,6 +70,7 @@
66
70
  --_ui-tabs-tab-min-block-size: var(--ui-tabs-tab-min-block-size);
67
71
  --_ui-tabs-tab-padding-block: var(--ui-tabs-tab-padding-block);
68
72
  --_ui-tabs-tab-padding-inline: var(--ui-tabs-tab-padding-inline);
73
+ --_ui-tabs-panel-padding: var(--ui-tabs-panel-padding);
69
74
 
70
75
  --_tab-min-block-size: var(--ui-control-size);
71
76
  --_tab-padding-block: var(--ui-space-2);
@@ -165,7 +170,7 @@
165
170
  min-inline-size: 0;
166
171
  }
167
172
  .panels > :slotted(*) {
168
- padding: var(--ui-space-4) !important;
173
+ padding: var(--_ui-tabs-panel-padding, var(--ui-space-4)) !important;
169
174
  }
170
175
 
171
176
  :host([orientation="vertical"]) {
@@ -17,14 +17,16 @@
17
17
  does not carry the meaning, so the words must say it too.</prop>
18
18
  <prop name="weight" type="keyword" values="inherit, normal, medium, semibold" default="inherit"
19
19
  >The weight. inherit keeps the surrounding weight; medium and semibold emphasise a name or a figure.</prop>
20
- <prop name="variant" type="keyword" values="body, eyebrow, code" default="body"
21
- >body is running text. eyebrow is a short, uppercase, tracked label that names what follows, such as a group of links; code sets a value to copy or read
22
- exactly, such as an address, in the monospace face.</prop>
20
+ <prop name="variant" type="keyword" values="body, display, heading, label, caption, eyebrow, code" default="body"
21
+ >body is running text. display is a page title, heading names a section, label identifies a control, and caption is supporting text. These roles set their
22
+ shared type scale, weight and leading; pair heading or display with an appropriate as heading element. eyebrow is a short, uppercase, tracked label that
23
+ names what follows, such as a group of links; code sets a value to copy or read exactly, such as an address, in the monospace face.</prop>
23
24
  <prop name="truncate" type="boolean" default="false">Keeps the text on one line, ending in an ellipsis when it does not fit.</prop>
24
25
  <prop name="align" type="keyword" values="start, center" default="start">Aligns the text within its line.</prop>
25
- <prop name="as" type="keyword" values="p, span, div, small, strong, label, h2, h3, h4, h5, h6" default="p"
26
+ <prop name="as" type="keyword" values="p, span, div, small, strong, label, h1, h2, h3, h4, h5, h6" default="p"
26
27
  >The native element the text renders: a paragraph by default, inline span, small, or strong within a line, a div for a block, a label for a control, or a
27
- heading, h2 to h6, below a Page Header's h1 and a Section's h2. A heading takes its size and weight from size and weight, like any other text.</prop>
28
+ heading, h1 to h6. Choose its document level independently from its visual variant. The default body variant keeps the existing size and weight
29
+ behavior.</prop>
28
30
  </defs>
29
31
  <template $match>
30
32
  <span $when="$as = 'span'"><slot></slot></span>
@@ -32,6 +34,7 @@
32
34
  <small $when="$as = 'small'"><slot></slot></small>
33
35
  <strong $when="$as = 'strong'"><slot></slot></strong>
34
36
  <label $when="$as = 'label'"><slot></slot></label>
37
+ <h1 $when="$as = 'h1'"><slot></slot></h1>
35
38
  <h2 $when="$as = 'h2'"><slot></slot></h2>
36
39
  <h3 $when="$as = 'h3'"><slot></slot></h3>
37
40
  <h4 $when="$as = 'h4'"><slot></slot></h4>
@@ -56,7 +59,7 @@
56
59
 
57
60
  /* A heading drops the browser's heading size and weight, so size and weight set it as they set
58
61
  any text, and inherit keeps the text around it. */
59
- :host:is(h2, h3, h4, h5, h6) {
62
+ :host:is(h1, h2, h3, h4, h5, h6) {
60
63
  font-family: var(--ui-font-display, inherit);
61
64
  font-size: inherit;
62
65
  font-weight: inherit;
@@ -119,10 +122,32 @@
119
122
  font-weight: var(--ui-font-semibold);
120
123
  }
121
124
 
125
+ :host([variant="display"]), :host([variant="heading"]) {
126
+ font-family: var(--ui-font-display, var(--ui-font-family-sans));
127
+ font-weight: var(--ui-font-semibold);
128
+ line-height: var(--ui-line-height-tight);
129
+ }
130
+ :host([variant="display"]) {
131
+ font-size: var(--ui-font-size-2xl);
132
+ }
133
+ :host([variant="heading"]) {
134
+ font-size: var(--ui-font-size-xl);
135
+ }
136
+ :host([variant="label"]) {
137
+ font-size: var(--ui-font-size-sm);
138
+ font-weight: var(--ui-font-medium);
139
+ line-height: var(--ui-line-height);
140
+ }
141
+ :host([variant="caption"]) {
142
+ font-size: var(--ui-font-size-sm);
143
+ line-height: var(--ui-line-height);
144
+ }
145
+
122
146
  :host([variant="eyebrow"]) {
123
147
  font-size: var(--ui-font-size-xs);
124
148
  font-weight: var(--ui-font-semibold);
125
149
  letter-spacing: 0.06em;
150
+ line-height: var(--ui-line-height);
126
151
  text-transform: uppercase;
127
152
  }
128
153
  :host([variant="code"]) {
@@ -81,6 +81,7 @@
81
81
  }
82
82
 
83
83
  :host {
84
+ --_ui-material-scale: 0.08;
84
85
  /* Hooks do not inherit, so the parts below read them from the root. */
85
86
  --_ui-tooltip-border: var(--ui-tooltip-border);
86
87
  --_ui-tooltip-max-inline-size: var(--ui-tooltip-max-inline-size);
@@ -133,6 +134,65 @@
133
134
  box-shadow: var(--_ui-tooltip-shadow, var(--ui-overlay-shadow, var(--ui-elevation-tooltip, var(--ui-shadow-md))));
134
135
  }
135
136
 
137
+ /* Purpose and measured size shape one shared separation lever. The content plane stays opaque;
138
+ transparency, saturation and edge light belong to the surrounding decorative material. */
139
+ .surface {
140
+ --_ui-material-glassiness: clamp(0, var(--ui-glassiness, 0), 1);
141
+ --_ui-material-separation: clamp(0, var(--ui-surface-separation, 0), 1);
142
+ --_ui-material-active: clamp(0, calc((var(--_ui-material-glassiness) + var(--_ui-material-separation)) * 100000), 1);
143
+ --_ui-material-rim: calc((var(--_ui-material-separation) + var(--_ui-material-glassiness) * 0.25) * var(--_ui-material-scale, 0.08) * 8px);
144
+ --_ui-material-content-color: var(--_ui-tooltip-surface, var(--ui-material-content, var(--ui-overlay-surface, var(--ui-surface-elevated))));
145
+ --_ui-material-rim-color: color-mix(in srgb, var(--ui-material-glass, var(--ui-surface-elevated)) calc(var(--_ui-material-active) * 100%), transparent);
146
+ background:
147
+ linear-gradient(var(--_ui-material-content-color), var(--_ui-material-content-color)) padding-box,
148
+ linear-gradient(var(--_ui-material-rim-color), var(--_ui-material-rim-color)) border-box,
149
+ var(--_ui-material-content-color) padding-box;
150
+ border-width: calc(var(--ui-border-width) + var(--_ui-material-rim));
151
+ border-color: var(
152
+ --_ui-tooltip-border,
153
+ color-mix(in oklab, var(--ui-overlay-border, var(--ui-border-strong)) calc((1 - var(--_ui-material-active)) * 100%), transparent)
154
+ );
155
+ box-shadow: var(
156
+ --_ui-tooltip-shadow,
157
+ var(
158
+ --ui-overlay-shadow,
159
+ 0 calc(var(--_ui-material-separation) * var(--_ui-material-scale, 0.08) * 8px) calc(
160
+ var(--_ui-material-separation) * var(--_ui-material-scale, 0.08) * 24px
161
+ ) color-mix(in oklab, var(--ui-text-primary) 15%, transparent),
162
+ inset 0 0 0 calc(var(--_ui-material-active) * var(--ui-border-width)) var(--ui-material-edge, var(--ui-border-strong)),
163
+ var(--ui-elevation-tooltip, var(--ui-shadow-md))
164
+ )
165
+ );
166
+ backdrop-filter: blur(calc(var(--_ui-material-glassiness) * var(--_ui-material-scale, 0.08) * 18px))
167
+ saturate(calc(1 + var(--_ui-material-glassiness) * var(--_ui-material-scale, 0.08) * 0.18));
168
+ -webkit-backdrop-filter: blur(calc(var(--_ui-material-glassiness) * var(--_ui-material-scale, 0.08) * 18px))
169
+ saturate(calc(1 + var(--_ui-material-glassiness) * var(--_ui-material-scale, 0.08) * 0.18));
170
+ }
171
+
172
+ @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
173
+ .surface {
174
+ background: var(--_ui-material-content-color);
175
+ }
176
+ }
177
+
178
+ @media (prefers-reduced-transparency: reduce) {
179
+ .surface {
180
+ background: var(--_ui-material-content-color);
181
+ backdrop-filter: none;
182
+ -webkit-backdrop-filter: none;
183
+ }
184
+ }
185
+
186
+ @media (forced-colors: active) {
187
+ .surface {
188
+ background: Canvas;
189
+ border-color: CanvasText;
190
+ box-shadow: none;
191
+ backdrop-filter: none;
192
+ -webkit-backdrop-filter: none;
193
+ }
194
+ }
195
+
136
196
  /* A keyboard shortcut follows the label after a thin divider, smaller and quieter, as menus show
137
197
  it. The label stays first in the description, so a screen reader hears the name, then the keys. */
138
198
  .shortcut {
@@ -160,7 +160,7 @@ export function connect(host) {
160
160
  attach();
161
161
  }
162
162
  surface?.destroy();
163
- surface = createAnchoredSurface(element, { anchor: trigger, placement: nextPlacement, gap: offset });
163
+ surface = createAnchoredSurface(element, { anchor: trigger, placement: nextPlacement, gap: offset, materialRole: "tooltip" });
164
164
  };
165
165
  const close = (reason, input) => {
166
166
  if (reason !== "escape" && reason !== "light-dismiss" && reason !== "programmatic") return;