@threadlabs/looma 0.34.0 → 0.35.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 (102) hide show
  1. package/components/ui-badge/ui-badge.html +11 -3
  2. package/components/ui-button/ui-button.html +29 -4
  3. package/components/ui-card/ui-card.html +15 -13
  4. package/components/ui-checkbox/ui-checkbox.html +4 -2
  5. package/components/ui-dialog/ui-dialog.html +2 -0
  6. package/components/ui-icon-button/ui-icon-button.html +22 -8
  7. package/components/ui-input/ui-input.html +2 -1
  8. package/components/ui-input-group/ui-input-group.html +4 -1
  9. package/components/ui-light-editor/ui-light-editor.html +45 -0
  10. package/components/ui-light-editor/ui-light-editor.js +53 -0
  11. package/components/ui-menu/ui-menu.html +2 -0
  12. package/components/ui-number-input/ui-number-input.html +2 -1
  13. package/components/ui-number-input/ui-number-input.js +6 -3
  14. package/components/ui-popover/ui-popover.html +2 -0
  15. package/components/ui-reel/ui-reel.html +2 -1
  16. package/components/ui-select/ui-select.html +9 -7
  17. package/components/ui-switch/ui-switch.html +3 -0
  18. package/components/ui-tabs/ui-tabs.html +2 -3
  19. package/components/ui-tabs/ui-tabs.js +0 -9
  20. package/components/ui-textarea/ui-textarea.html +2 -1
  21. package/components/ui-tooltip/ui-tooltip.html +2 -1
  22. package/dist/index.js +24 -23
  23. package/docs/README.md +29 -3
  24. package/docs/card.html +7 -6
  25. package/docs/catalog-view.mjs +2 -1
  26. package/docs/catalog.json +575 -53
  27. package/docs/customizer-render.mjs +31 -16
  28. package/docs/customizer.js +72 -19
  29. package/docs/index.d.ts +4 -1
  30. package/docs/index.mjs +1 -1
  31. package/docs/render.mjs +1 -1
  32. package/docs/site.mjs +33 -22
  33. package/docs/theme-session.mjs +4 -2
  34. package/docs/theme.mjs +61 -7
  35. package/package.json +2 -2
  36. package/styles/ui-badge.css +11 -6
  37. package/styles/ui-button.css +29 -4
  38. package/styles/ui-card.css +9 -9
  39. package/styles/ui-checkbox.css +4 -2
  40. package/styles/ui-dialog.css +4 -0
  41. package/styles/ui-icon-button.css +22 -8
  42. package/styles/ui-input-group.css +4 -1
  43. package/styles/ui-input.css +2 -1
  44. package/styles/ui-light-editor.css +28 -0
  45. package/styles/ui-menu.css +2 -0
  46. package/styles/ui-popover.css +2 -0
  47. package/styles/ui-select.css +9 -7
  48. package/styles/ui-switch.css +3 -0
  49. package/styles/ui-tabs.css +2 -3
  50. package/styles/ui-textarea.css +2 -1
  51. package/styles/ui-tooltip.css +2 -1
  52. package/theme-light.css +1 -0
  53. package/theme-system.css +43 -7
  54. package/tokens.css +5 -1
  55. package/vanilla/UiCard.d.ts +2 -1
  56. package/vanilla/UiCard.js +12 -12
  57. package/vanilla/UiLightEditor.d.ts +29 -0
  58. package/vanilla/UiLightEditor.js +102 -0
  59. package/vanilla/UiNumberInput.js +27 -20
  60. package/vanilla/index.js +1 -0
  61. package/vue/UiBadge.vue +29 -4
  62. package/vue/UiButton.vue +33 -4
  63. package/vue/UiCard.d.ts +8 -2
  64. package/vue/UiCard.vue +41 -17
  65. package/vue/UiCheckbox.vue +4 -2
  66. package/vue/UiDialog.vue +4 -0
  67. package/vue/UiIconButton.vue +26 -11
  68. package/vue/UiInput.vue +2 -1
  69. package/vue/UiInputGroup.vue +4 -1
  70. package/vue/UiLightEditor.d.ts +75 -0
  71. package/vue/UiLightEditor.js +2 -0
  72. package/vue/UiLightEditor.vue +402 -0
  73. package/vue/UiMenu.vue +2 -0
  74. package/vue/UiNumberInput.vue +32 -3
  75. package/vue/UiPopover.vue +2 -0
  76. package/vue/UiReel.vue +1 -1
  77. package/vue/UiSelect.vue +16 -10
  78. package/vue/UiSwitch.vue +3 -0
  79. package/vue/UiTabs.vue +2 -3
  80. package/vue/UiTextarea.vue +2 -1
  81. package/vue/UiTooltip.vue +2 -1
  82. package/vue/chunks/UiBadge.js +1 -1
  83. package/vue/chunks/UiButton.js +1 -1
  84. package/vue/chunks/UiCard.js +33 -5
  85. package/vue/chunks/UiCheckbox.js +1 -1
  86. package/vue/chunks/UiDialog.js +1 -1
  87. package/vue/chunks/UiIconButton.js +1 -1
  88. package/vue/chunks/UiInput.js +1 -1
  89. package/vue/chunks/UiInputGroup.js +1 -1
  90. package/vue/chunks/UiLightEditor.js +347 -0
  91. package/vue/chunks/UiMenu.js +1 -1
  92. package/vue/chunks/UiNumberInput.js +21 -8
  93. package/vue/chunks/UiPopover.js +1 -1
  94. package/vue/chunks/UiReel.js +2 -2
  95. package/vue/chunks/UiSelect.js +1 -1
  96. package/vue/chunks/UiSwitch.js +1 -1
  97. package/vue/chunks/UiTabs.js +1 -1
  98. package/vue/chunks/UiTextarea.js +1 -1
  99. package/vue/chunks/UiTooltip.js +1 -1
  100. package/vue/components.css +312 -71
  101. package/vue/index.d.ts +1 -0
  102. package/vue/index.js +2 -1
package/docs/theme.mjs CHANGED
@@ -27,6 +27,12 @@ export const themeFields = [
27
27
  ['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.'],
28
28
  ['Material', 'translucency', 'Overlay translucency', '--ui-overlay-translucency', 'fraction', '0', 'How much background comes through floating surfaces.'],
29
29
  ['Material', 'edge-light', 'Edge light', '--ui-edge-light', 'fraction', '0', 'Light caught by surface edges, independent of depth.'],
30
+ ['Material', 'finish', 'Finish', '--ui-finish', 'fraction', '0', 'Matte through polished: how strongly the material catches a reflection.'],
31
+ ['Material', 'glow', 'Glow', '--ui-glow', 'fraction', '0', 'Soft reflected light beyond a surface, derived from its tone.'],
32
+ ['Material', 'light-x', 'Light horizontal position', '--ui-light-x', 'fraction', '0.5', 'The light source from left to right, relative to each surface.'],
33
+ ['Material', 'light-y', 'Light vertical position', '--ui-light-y', 'fraction', '0', 'The light source from top to bottom, relative to each surface.'],
34
+ ['Material', 'light-spread', 'Light spread', '--ui-light-spread', 'fraction', '0.5', 'A concentrated spot through broad illumination.'],
35
+ ['Material', 'light-strength', 'Light strength', '--ui-light-strength', 'fraction', '0.65', 'Shared illumination; each material and component role determines its response.'],
30
36
  ['Motion', 'response-time', 'Response time', '--ui-motion-fast', 'time', '120ms', 'Shared feedback pace; floating transitions take proportionally longer.'],
31
37
  ['Motion', 'response-curve', 'Response curve', '--ui-motion-ease', 'easing', 'cubic-bezier(0.2,0,0.2,1)', 'How movement accelerates and settles.'],
32
38
  ].map(([family, id, label, property, type, defaultValue, description]) => Object.freeze({ family, id, label, property, type, defaultValue, description, ...(bounds[id] ? { min: bounds[id][0], max: bounds[id][1] } : type === 'fraction' ? { min: 0, max: 1 } : {}) }));
@@ -35,14 +41,12 @@ export const defaultRecipe = () => ({ schemaVersion: 1, values: Object.fromEntri
35
41
  const starting = values => ({ schemaVersion: 1, values: { ...defaultRecipe().values, ...values } });
36
42
  export const startingPoints = {
37
43
  clear: defaultRecipe(),
38
- bookish: starting({ action: '#2e1228', canvas: '#f7f7f5', surface: '#fffefd', text: '#0d0c0c', 'body-font': 'Arial, sans-serif', 'display-font': 'Georgia, serif', 'text-weight': '400', 'control-size': '42px', 'scale-contrast': '1.18', 'outer-depth': '1', 'inner-depth': '1', glassiness: '0.8', 'surface-separation': '0.65', translucency: '0.18', 'edge-light': '1', 'response-time': '150ms', 'response-curve': 'ease' }),
44
+ bookish: starting({ action: '#2e1228', canvas: '#f7f7f5', surface: '#fffefd', text: '#0d0c0c', 'body-font': 'Arial, sans-serif', 'display-font': 'Georgia, serif', 'text-weight': '400', 'control-size': '42px', 'scale-contrast': '1.18', 'outer-depth': '1', 'inner-depth': '1', glassiness: '0.8', 'surface-separation': '0.65', translucency: '0.18', 'edge-light': '1', finish: '0.85', glow: '0.3', 'light-spread': '0.3', 'light-strength': '0.9', 'response-time': '150ms', 'response-curve': 'ease' }),
39
45
  wiki: starting({ action: '#4f46e5', canvas: '#ffffff', surface: '#ffffff', text: '#1a1a1a', 'control-size': '34px', density: '0.85', 'scale-contrast': '1.15', 'corner-roundness': '6px', 'outer-depth': '0.35', 'inner-depth': '0', 'response-time': '110ms' }),
40
46
  editorial: starting({ action: '#182c24', canvas: '#eee9dc', surface: '#fffdf5', text: '#182c24', 'corner-roundness': '0px', 'outer-depth': '0', 'inner-depth': '0', 'body-font': 'Georgia, serif', 'display-font': 'Georgia, serif', 'text-weight': '400', 'line-weight': '2px', 'response-time': '90ms', 'response-curve': 'linear' }),
41
47
  night: starting({ action: '#a7b8ff', canvas: '#11141c', surface: '#202634', text: '#edf1fb', 'corner-roundness': '12px', 'outer-depth': '1.4', 'inner-depth': '0.45', translucency: '0.2', 'edge-light': '0.65', 'response-time': '210ms', 'response-curve': 'cubic-bezier(0.2,0.8,0.2,1)' }),
42
48
  garden: starting({ action: '#256342', canvas: '#eef5e7', surface: '#fcfff6', text: '#173121', 'corner-roundness': '22px', 'outer-depth': '0.35', 'inner-depth': '0', 'edge-light': '0.2', 'control-size': '46px', 'scale-contrast': '1.24', density: '1.05', 'response-time': '180ms', 'response-curve': 'cubic-bezier(0.16,1,0.3,1)' }),
43
49
  };
44
- // Preserve the public historical startingPoints.library import without a duplicate UI preset.
45
- Object.defineProperty(startingPoints, 'library', { value: startingPoints.bookish });
46
50
  export const startingPointLabels = { clear: 'Clear', bookish: 'Bookish', wiki: 'Wiki', editorial: 'Editorial', night: 'Night', garden: 'Garden' };
47
51
  const paletteKeys = ['action', 'canvas', 'surface', 'text'];
48
52
  // Palette choices move only the shared color seeds; size, material and motion stay independent.
@@ -51,6 +55,33 @@ export const themePalettes = Object.fromEntries([
51
55
  ['editorial', 'Ink & parchment'], ['night', 'Midnight'], ['garden', 'Forest & cream'],
52
56
  ].map(([id, label]) => [id, { label, values: Object.fromEntries(paletteKeys.map(key => [key, startingPoints[id].values[key]])) }]));
53
57
 
58
+ const materialKeys = themeFields.filter(field => field.family === 'Material').map(field => field.id);
59
+ const materialDefaults = defaultRecipe().values;
60
+ const material = (label, overrides) => ({ label, values: Object.fromEntries(materialKeys.map(key => [key, overrides[key] ?? materialDefaults[key]])) });
61
+ export const materialPresets = {
62
+ matte: material('Matte', { 'outer-depth': '0', 'inner-depth': '0' }),
63
+ soft: material('Soft', { 'outer-depth': '0.6', 'inner-depth': '0.3', 'edge-light': '0.3', finish: '0.2', 'light-spread': '0.8' }),
64
+ polished: material('Polished', { 'outer-depth': '1', 'inner-depth': '1', 'edge-light': '1', finish: '0.85', glow: '0.3', 'light-spread': '0.3', 'light-strength': '0.9' }),
65
+ glass: material('Frosted glass', startingPoints.bookish.values),
66
+ };
67
+ const refinementPresets = family => family === 'material' ? materialPresets : family === 'palette' ? themePalettes : undefined;
68
+
69
+ /** Preset provenance is optional recipe data, never a second source of theme values. */
70
+ export function refinementState(input, family) {
71
+ const recipe = validateRecipe(input), presets = refinementPresets(family);
72
+ if (!presets) throw new Error('Unknown refinement family.');
73
+ const matches = preset => Object.entries(preset.values).every(([key, value]) => recipe.values[key] === value);
74
+ const id = recipe.presets?.[family] ?? Object.keys(presets).find(id => matches(presets[id])) ?? 'custom';
75
+ return { id, customized: id !== 'custom' && !matches(presets[id]) };
76
+ }
77
+
78
+ /** Apply or restore one family without changing the rest of the design system. */
79
+ export function applyRefinement(input, family, id) {
80
+ const recipe = validateRecipe(input), preset = refinementPresets(family)?.[id];
81
+ if (!preset) throw new Error('Unknown refinement preset.');
82
+ return validateRecipe({ ...recipe, values: { ...recipe.values, ...preset.values }, presets: { ...recipe.presets, [family]: id } });
83
+ }
84
+
54
85
  /** Identify a coordinated palette without coupling it to the rest of a recipe. */
55
86
  export function paletteFor(input) {
56
87
  const { values } = validateRecipe(input);
@@ -111,19 +142,28 @@ export function validateRecipe(input) {
111
142
  if (number !== undefined && (number < min || number > max)) throw new Error(`${field.label}: choose a value between ${min} and ${max}${field.type.includes('length') ? 'px (or equivalent)' : field.type === 'time' ? 'ms' : ''}.`);
112
143
  }
113
144
  }
114
- return { schemaVersion: 1, values };
145
+ if (input.presets !== undefined && (!input.presets || typeof input.presets !== 'object' || Array.isArray(input.presets) || Object.entries(input.presets).some(([family, id]) => !Object.hasOwn(refinementPresets(family) ?? {}, id)))) throw new Error('Unknown refinement preset.');
146
+ return { schemaVersion: 1, values, ...(input.presets === undefined ? {} : { presets: { ...input.presets } }) };
115
147
  }
116
148
 
117
149
  /** Match presets by their complete semantic recipe, including locally saved themes. */
118
150
  export function startingPointFor(input) {
119
- const recipe = JSON.stringify(validateRecipe(input));
120
- return Object.entries(startingPoints).find(([, value]) => JSON.stringify(value) === recipe)?.[0] ?? 'custom';
151
+ const recipe = JSON.stringify(validateRecipe(input).values);
152
+ return Object.entries(startingPoints).find(([, value]) => JSON.stringify(value.values) === recipe)?.[0] ?? 'custom';
121
153
  }
122
154
 
123
155
  const rgb = hex => [1, 3, 5].map(i => parseInt(hex.slice(i, i + 2), 16));
124
156
  const mix = (a, b, weight) => '#' + rgb(a).map((n, i) => Math.round(n * weight + rgb(b)[i] * (1 - weight)).toString(16).padStart(2, '0')).join('');
125
157
  const luminance = hex => rgb(hex).map(n => { const s = n / 255; return s <= .04045 ? s / 12.92 : ((s + .055) / 1.055) ** 2.4; }).reduce((sum, n, i) => sum + n * [.2126, .7152, .0722][i], 0);
126
158
  const contrast = (a, b) => (Math.max(luminance(a), luminance(b)) + .05) / (Math.min(luminance(a), luminance(b)) + .05);
159
+ // The brightest reflection stop is itself readable. Interpolating it over a readable
160
+ // fill cannot wash out the label, even at maximum finish and light strength.
161
+ function reflection(fill, text) {
162
+ const labels = Array.isArray(text) ? text : [text];
163
+ let amount = .32;
164
+ while (amount > 0 && labels.some(label => contrast(mix('#ffffff', fill, amount), label) < 4.5)) amount = Math.max(0, amount - .005);
165
+ return mix('#ffffff', fill, amount);
166
+ }
127
167
  const foreground = (fill, light, dark) => [light, dark, '#ffffff', '#000000'].sort((a, b) => contrast(fill, b) - contrast(fill, a))[0];
128
168
 
129
169
  /** Find the nearest mixture with a known readable endpoint, including every supplied surface. */
@@ -164,6 +204,7 @@ export function deriveTheme(input, { mode } = {}) {
164
204
  tokens[`--ui-${tone}`] = fill;
165
205
  tokens[`--ui-on-${tone}`] = foreground(fill, '#ffffff', '#000000');
166
206
  tokens[`--ui-${tone}-on-strong`] = tokens[`--ui-on-${tone}`];
207
+ tokens[`--ui-${tone}-reflection`] = reflection(fill, tokens[`--ui-on-${tone}`]);
167
208
  tokens[`--ui-${tone}-hover`] = mix(fill, ink, .86);
168
209
  tokens[`--ui-${tone}-active`] = mix(fill, ink, .74);
169
210
  tokens[`--ui-${tone}-soft`] = mix(fill, tokens['--ui-surface-raised'], .08);
@@ -177,10 +218,17 @@ export function deriveTheme(input, { mode } = {}) {
177
218
  tokens['--ui-text'] = readableColour(textSeed, readingSurfaces, 4.5, ink);
178
219
  tokens['--ui-text-secondary'] = readableColour(mix(tokens['--ui-text'], tokens['--ui-surface-raised'], .72), readingSurfaces, 4.5, ink);
179
220
  tokens['--ui-text-muted'] = tokens['--ui-text-secondary'];
221
+ for (const [state, amount] of [['surface', .06], ['hover', .1], ['active', .14]]) {
222
+ tokens[`--ui-neutral-${state}`] = readableColour(mix(tokens['--ui-text'], tokens['--ui-surface-raised'], amount), [tokens['--ui-text']], 4.5, paper);
223
+ }
224
+ tokens['--ui-surface-reflection'] = reflection(tokens['--ui-surface-raised'], [tokens['--ui-text'], tokens['--ui-text-secondary'], ...Object.keys(tones).map(tone => tokens[`--ui-${tone}-subtle-text`])]);
225
+ tokens['--ui-neutral-reflection'] = reflection(tokens['--ui-neutral-surface'], tokens['--ui-text']);
180
226
  tokens['--ui-selection-surface'] = mix(tokens['--ui-accent'], tokens['--ui-surface-raised'], .1);
181
227
  tokens['--ui-selection-text'] = tokens['--ui-accent-subtle-text'];
182
228
  tokens['--ui-selection-border'] = mix(tokens['--ui-accent'], tokens['--ui-surface-raised'], .2);
183
229
  tokens['--ui-control-surface'] = tokens['--ui-surface-raised'];
230
+ // A hint is quieter than body copy; resolve it against its field, not every tinted content surface.
231
+ tokens['--ui-control-placeholder'] = readableColour(mix(tokens['--ui-text'], tokens['--ui-control-surface'], .5), [tokens['--ui-control-surface']], 4.5, ink);
184
232
  tokens['--ui-control-border'] = readableColour(mix(tokens['--ui-text'], tokens['--ui-surface-raised'], .43), readingSurfaces, 3, ink);
185
233
  tokens['--ui-control-border-hover'] = tokens['--ui-control-border'];
186
234
  tokens['--ui-border-strong'] = tokens['--ui-control-border'];
@@ -212,8 +260,14 @@ export function themeCSS(input, systemCSS = '', { scope = 'portable' } = {}) {
212
260
  else geometry.push(` ${name}: ${value};`);
213
261
  }
214
262
  const geometrySelector = scope === 'documentation' ? ':root[data-looma-documentation-theme]' : ':root';
263
+ const chevron = color => `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='${color.replace('#', '%23')}' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")`;
264
+ const boundary = mode => scope === 'documentation'
265
+ ? `:root[data-looma-documentation-theme][data-theme="${mode}"], :root[data-looma-documentation-theme] [data-theme="${mode}"]`
266
+ : `[data-theme="${mode}"]`;
267
+ const chevrons = `${geometrySelector} { --ui-select-chevron: ${chevron(light['--ui-text-secondary'])}; }\n@media (prefers-color-scheme: dark) { ${geometrySelector}:not([data-theme]) { --ui-select-chevron: ${chevron(dark['--ui-text-secondary'])}; } }\n${boundary('light')} { --ui-select-chevron: ${chevron(light['--ui-text-secondary'])}; }\n${boundary('dark')} { --ui-select-chevron: ${chevron(dark['--ui-text-secondary'])}; }`;
268
+
215
269
  // Resolve colors at their point of use, so a nearer mode boundary wins independently
216
270
  // of the selector specificity needed to restore an early documentation theme.
217
271
  // Keep the first comment stable: the local writer recognizes its generated files by this marker.
218
- return `/* Looma shared semantic theme. */\n/* Always includes light and dark.\n Set data-theme="light" or "dark" on any element to fix that subtree's mode.\n Leave the root attribute unset to follow the system; a switch can set/remove it. */\n${systemCSS}\n${geometrySelector} {\n${geometry.join('\n')}\n}\n${selector} {\n${palette.join('\n')}\n}\n:root:not([data-theme]) { color-scheme: light dark; }\n[data-theme="light"] { color-scheme: light; }\n[data-theme="dark"] { color-scheme: dark; }\n@media (prefers-reduced-motion: reduce) {\n ${selector} { --ui-motion-fast: 0ms; --ui-motion-base: 0ms; --ui-motion-press: 0ms; --ui-press-scale: 1; }\n}\n`;
272
+ return `/* Looma shared semantic theme. */\n/* Always includes light and dark.\n Set data-theme="light" or "dark" on any element to fix that subtree's mode.\n Leave the root attribute unset to follow the system; a switch can set/remove it. */\n${systemCSS}\n${geometrySelector} {\n${geometry.join('\n')}\n}\n${selector} {\n${palette.join('\n')}\n}\n${chevrons}\n:root:not([data-theme]) { color-scheme: light dark; }\n[data-theme="light"] { color-scheme: light; }\n[data-theme="dark"] { color-scheme: dark; }\n@media (prefers-reduced-motion: reduce) {\n ${selector} { --ui-motion-fast: 0ms; --ui-motion-base: 0ms; --ui-motion-press: 0ms; --ui-press-scale: 1; }\n}\n@media (prefers-reduced-transparency: reduce), (forced-colors: active) {\n ${selector} { --ui-finish: 0; --ui-glow: 0; }\n}\n`;
219
273
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threadlabs/looma",
3
- "version": "0.34.0",
3
+ "version": "0.35.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.3",
130
+ "markupress": "^0.8.5",
131
131
  "@nextwebwg/htmlkit": "1.0.0-alpha.69",
132
132
  "vite": "^8.3.4"
133
133
  },
@@ -295,10 +295,18 @@
295
295
  box-shadow: none;
296
296
  }
297
297
 
298
- /* Neutral reverses a subtle surface/ink pair rather than a strong tone. Keep its original
299
- surface ink: moving it toward dark-theme primary text would reduce overlay contrast. */
300
298
  :scope:where([data-component~="ui-badge"])[data-ui-badge-state~="variant=inverse"][data-ui-badge-state~="tone=neutral"] {
301
- color: var(--ui-badge-text, var(--_badge-color-text, var(--_badge-surface)));
299
+ --_badge-outline-surface: var(--ui-badge-surface, var(--_badge-color-surface, color-mix(in srgb, var(--ui-surface-raised) 88%, transparent)));
300
+ color: var(--ui-badge-text, var(--_badge-color-text, var(--ui-text-primary)));
301
+ }
302
+ :scope:where([data-component~="ui-badge"])[data-ui-badge-state~="variant=inverse"][data-ui-badge-state~="tone=neutral"]:not([data-ui-badge-state~="shape=circle"]) {
303
+ border-color: var(--ui-badge-border, var(--_badge-color-border, var(--_badge-outline-surface)));
304
+ }
305
+ :scope:where([data-component~="ui-badge"])[data-ui-badge-state~="variant=inverse"][data-ui-badge-state~="tone=neutral"][data-ui-badge-state~="shape=circle"] {
306
+ --_badge-outline-surface: var(--ui-badge-surface, var(--_badge-color-surface, light-dark(
307
+ color-mix(in srgb, var(--ui-surface-raised) var(--ui-badge-circle-opacity, 66%), transparent),
308
+ color-mix(in srgb, var(--ui-surface-raised) var(--ui-badge-circle-opacity, 56%), transparent)
309
+ )));
302
310
  }
303
311
 
304
312
  :scope:where([data-component~="ui-badge"])[data-ui-badge-state~="variant=outline"] {
@@ -542,9 +550,6 @@
542
550
  /* A count can keep its containing control's contrasting ink instead of reversing it.
543
551
  The faint fill preserves text contrast; a quiet boundary separates the count from its control. */
544
552
 
545
- /* Neutral reverses a subtle surface/ink pair rather than a strong tone. Keep its original
546
- surface ink: moving it toward dark-theme primary text would reduce overlay contrast. */
547
-
548
553
  /* An icon mark keeps the existing tone recipe and follows the global spacing and radius. */
549
554
 
550
555
  :scope:where([data-component~="ui-badge"])[data-ui-badge-state~="shape=square"] .label :where([data-slotted], [data-slotted] *):is(svg) {
@@ -84,7 +84,7 @@
84
84
  :scope:where([data-component~="ui-button"]) {
85
85
  /* Optional inherited action-group values keep text and icon actions under the same light.
86
86
  One fine top/start highlight follows the bottom/end lowlight at a joined action seam.
87
- A single ambient lift avoids stacking a face gradient or another inset bevel. */
87
+ The shared theme supplies reflections and glow independently of the resting lift. */
88
88
  --_ui-action-start: 1px;
89
89
  --_ui-action-edge: var(
90
90
  --ui-action-edge,
@@ -92,6 +92,8 @@
92
92
  inset calc(-1 * var(--_ui-action-start)) -1px 0 light-dark(rgb(0 0 0 / 0.08), rgb(0 0 0 / 0.08))
93
93
  );
94
94
  --_ui-action-shadow: var(--_ui-action-edge), 0 1px 2px light-dark(rgb(0 0 0 / 0.10), rgb(0 0 0 / 0.20));
95
+ --_tone-highlight: var(--ui-accent-highlight, none);
96
+ --_tone-glow: var(--ui-accent-glow, 0 0 transparent);
95
97
  --_hue: var(--ui-accent);
96
98
  --_hue-hover: var(--ui-accent-hover);
97
99
  --_hue-active: var(--ui-accent-active);
@@ -142,8 +144,8 @@
142
144
  white-space: var(--ui-button-white-space, inherit);
143
145
  line-height: var(--ui-line-height-tight);
144
146
  padding: var(--ui-control-inset-block-md, var(--ui-space-2)) var(--ui-action-inset-md, var(--ui-space-3)) !important;
145
- background-image: none;
146
- box-shadow: var(--ui-button-shadow, var(--ui-action-shadow, var(--_ui-action-shadow)));
147
+ background-image: var(--ui-surface-highlight, none);
148
+ box-shadow: var(--ui-button-shadow, var(--ui-action-shadow, var(--_ui-action-shadow))), var(--_tone-glow);
147
149
  cursor: pointer;
148
150
  transition:
149
151
  scale var(--ui-motion-press, var(--ui-motion-fast)) var(--ui-motion-ease),
@@ -275,7 +277,8 @@
275
277
  border-color: var(--_tone);
276
278
  background-color: var(--_tone);
277
279
  color: var(--_on-tone);
278
- box-shadow: var(--ui-button-solid-shadow, var(--ui-action-shadow, var(--_ui-action-shadow)));
280
+ background-image: var(--_tone-highlight);
281
+ box-shadow: var(--ui-button-solid-shadow, var(--ui-action-shadow, var(--_ui-action-shadow))), var(--_tone-glow);
279
282
  }
280
283
 
281
284
  :is(:scope:where([data-component~="ui-button"])[data-appearance="solid"], :scope:where([data-component~="ui-button"])[data-appearance="danger"]):hover:not(:disabled, [aria-disabled="true"]) {
@@ -293,6 +296,8 @@
293
296
  /* Tone is the colour, variant is the volume. Every pairing works: a danger outline is a
294
297
  destructive secondary action, not a special case. */
295
298
  :scope:where([data-component~="ui-button"])[data-tone="neutral"] {
299
+ --_tone-highlight: var(--ui-neutral-highlight, none);
300
+ --_tone-glow: var(--ui-neutral-glow, 0 0 transparent);
296
301
  --_hue: var(--ui-text);
297
302
  --_hue-hover: var(--ui-text);
298
303
  --_hue-active: var(--ui-text);
@@ -300,7 +305,21 @@
300
305
  --_on-hue: var(--ui-surface);
301
306
  }
302
307
 
308
+ :scope:where([data-component~="ui-button"])[data-tone="neutral"][data-appearance="solid"] {
309
+ border-color: var(--ui-button-border, var(--ui-control-border));
310
+ background-color: var(--ui-button-surface, var(--ui-neutral-surface));
311
+ color: var(--ui-button-text, var(--ui-text));
312
+ }
313
+ :scope:where([data-component~="ui-button"])[data-tone="neutral"][data-appearance="solid"]:hover:not(:disabled, [aria-disabled="true"]) {
314
+ background-color: var(--ui-button-hover-surface, var(--ui-neutral-hover));
315
+ }
316
+ :scope:where([data-component~="ui-button"])[data-tone="neutral"][data-appearance="solid"]:active:not(:disabled, [aria-disabled="true"]) {
317
+ background-color: var(--ui-button-active-surface, var(--ui-neutral-active));
318
+ }
319
+
303
320
  :scope:where([data-component~="ui-button"])[data-tone="danger"], :scope:where([data-component~="ui-button"])[data-appearance="danger"] {
321
+ --_tone-highlight: var(--ui-danger-highlight, none);
322
+ --_tone-glow: var(--ui-danger-glow, 0 0 transparent);
304
323
  --_hue: var(--ui-danger);
305
324
  --_hue-hover: var(--ui-danger-hover);
306
325
  --_hue-active: var(--ui-danger-active, var(--ui-danger-hover));
@@ -309,6 +328,8 @@
309
328
  }
310
329
 
311
330
  :scope:where([data-component~="ui-button"])[data-tone="success"] {
331
+ --_tone-highlight: var(--ui-success-highlight, none);
332
+ --_tone-glow: var(--ui-success-glow, 0 0 transparent);
312
333
  --_hue: var(--ui-success);
313
334
  --_hue-hover: var(--ui-success-hover);
314
335
  --_hue-active: var(--ui-success-active);
@@ -317,6 +338,8 @@
317
338
  }
318
339
 
319
340
  :scope:where([data-component~="ui-button"])[data-tone="warning"] {
341
+ --_tone-highlight: var(--ui-warning-highlight, none);
342
+ --_tone-glow: var(--ui-warning-glow, 0 0 transparent);
320
343
  --_hue: var(--ui-warning);
321
344
  --_hue-hover: var(--ui-warning-hover);
322
345
  --_hue-active: var(--ui-warning-active);
@@ -325,6 +348,8 @@
325
348
  }
326
349
 
327
350
  :scope:where([data-component~="ui-button"])[data-tone="info"] {
351
+ --_tone-highlight: var(--ui-info-highlight, none);
352
+ --_tone-glow: var(--ui-info-glow, 0 0 transparent);
328
353
  --_hue: var(--ui-info);
329
354
  --_hue-hover: var(--ui-info-hover);
330
355
  --_hue-active: var(--ui-info-active);
@@ -11,6 +11,7 @@
11
11
  needs no layout of its own. */
12
12
  :scope:where([data-component~="ui-card"]) {
13
13
  --_ui-card-surface: var(--ui-surface-raised);
14
+ --_ui-card-elevation: 0 0 0 transparent;
14
15
  --_ui-selection-context-surface: initial;
15
16
  --_ui-selection-context-border: initial;
16
17
  --_ui-selection-context-text: initial;
@@ -45,33 +46,36 @@
45
46
  }
46
47
 
47
48
  :scope:where([data-component~="ui-card"])[data-ui-card-state~="variant=elevated"] {
49
+ --_ui-card-elevation: var(--ui-shadow-lg);
48
50
  border-color: var(--ui-border-subtle);
49
51
  box-shadow: var(--ui-shadow-lg);
50
52
  }
51
53
 
52
- :scope:where([data-component~="ui-card"])[data-ui-card-state~="variant=glass"] {
54
+ :scope:where([data-component~="ui-card"])[data-ui-card-state~="treatment=expressive"] {
53
55
  --_ui-panel-glass: clamp(0, var(--ui-glassiness, 0), 1);
54
56
  --_ui-panel-separation: clamp(0, var(--ui-surface-separation, 0), 1);
55
57
  --_ui-panel-rim: calc((var(--_ui-panel-separation) + var(--_ui-panel-glass) * 0.25) * 0.65 * 8px);
56
58
  border-width: calc(var(--ui-border-width) + var(--_ui-panel-rim));
57
59
  border-color: transparent;
58
60
  background:
61
+ var(--ui-surface-highlight, linear-gradient(transparent, transparent)) padding-box,
59
62
  linear-gradient(var(--_ui-card-surface), var(--_ui-card-surface)) padding-box,
60
63
  linear-gradient(var(--ui-material-glass, var(--ui-surface-raised)), var(--ui-material-glass, var(--ui-surface-raised))) border-box,
61
64
  var(--_ui-card-surface) padding-box;
62
65
  box-shadow:
63
- var(--ui-shadow-lg),
66
+ var(--_ui-card-elevation),
67
+ var(--ui-material-glow, 0 0 transparent),
64
68
  inset 0 0 0 var(--ui-border-width) var(--ui-material-edge, var(--ui-border-strong));
65
69
  backdrop-filter: blur(calc(var(--_ui-panel-glass) * 0.65 * 18px)) saturate(calc(1 + var(--_ui-panel-glass) * 0.65 * 0.18));
66
70
  -webkit-backdrop-filter: blur(calc(var(--_ui-panel-glass) * 0.65 * 18px)) saturate(calc(1 + var(--_ui-panel-glass) * 0.65 * 0.18));
67
71
  }
68
72
  @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
69
- :scope:where([data-component~="ui-card"])[data-ui-card-state~="variant=glass"] {
73
+ :scope:where([data-component~="ui-card"])[data-ui-card-state~="treatment=expressive"] {
70
74
  background: var(--_ui-card-surface);
71
75
  }
72
76
  }
73
77
  @media (prefers-reduced-transparency: reduce) {
74
- :scope:where([data-component~="ui-card"])[data-ui-card-state~="variant=glass"] {
78
+ :scope:where([data-component~="ui-card"])[data-ui-card-state~="treatment=expressive"] {
75
79
  background: var(--_ui-card-surface);
76
80
  border-width: var(--ui-border-width);
77
81
  border-color: var(--ui-border-strong);
@@ -80,7 +84,7 @@
80
84
  }
81
85
  }
82
86
  @media (forced-colors: active) {
83
- :scope:where([data-component~="ui-card"])[data-ui-card-state~="variant=glass"] {
87
+ :scope:where([data-component~="ui-card"])[data-ui-card-state~="treatment=expressive"] {
84
88
  background: Canvas;
85
89
  color: CanvasText;
86
90
  border: var(--ui-border-width) solid CanvasText;
@@ -116,10 +120,6 @@
116
120
 
117
121
  :scope:where([data-component~="ui-card"])[data-ui-card-state~="tone=danger"] {
118
122
  border-color: color-mix(in srgb, var(--ui-danger) 45%, var(--ui-border-default));
119
- border-inline-start: var(--ui-accent-line-width) solid var(--ui-danger);
120
- /* An accent edge keeps the corners on its side straight. */
121
- border-start-start-radius: 0;
122
- border-end-start-radius: 0;
123
123
  --_ui-card-surface: var(--ui-danger-soft);
124
124
  }
125
125
 
@@ -91,7 +91,8 @@
91
91
  margin: max(0px, calc((1lh - var(--_ui-checkbox-size, 1.125rem)) / 2)) 0 0 !important;
92
92
  border: var(--ui-border-width) solid var(--ui-control-border, var(--ui-border-strong));
93
93
  border-radius: min(var(--ui-radius-sm), calc(var(--_ui-checkbox-size, 1.125rem) / 4));
94
- background: var(--ui-control-surface, var(--ui-surface-default));
94
+ background-color: var(--ui-control-surface, var(--ui-surface-default));
95
+ background-image: var(--ui-surface-highlight, none);
95
96
  box-shadow: var(--ui-control-inset);
96
97
  cursor: pointer;
97
98
  transition:
@@ -110,7 +111,8 @@
110
111
  input:checked,
111
112
  input:indeterminate {
112
113
  border-color: var(--ui-accent-solid);
113
- background: var(--ui-accent-solid);
114
+ background-color: var(--ui-accent-solid);
115
+ background-image: var(--ui-accent-highlight, none);
114
116
  box-shadow: var(--ui-control-inset);
115
117
  }
116
118
 
@@ -81,6 +81,7 @@
81
81
  --_ui-material-content-color: var(--ui-dialog-surface, var(--ui-material-content, var(--ui-overlay-surface, var(--ui-surface-elevated))));
82
82
  --_ui-material-rim-color: color-mix(in srgb, var(--ui-material-glass, var(--ui-surface-elevated)) calc(var(--_ui-material-active) * 100%), transparent);
83
83
  background:
84
+ var(--ui-surface-highlight, linear-gradient(transparent, transparent)) padding-box,
84
85
  linear-gradient(var(--_ui-material-content-color), var(--_ui-material-content-color)) padding-box,
85
86
  linear-gradient(var(--_ui-material-rim-color), var(--_ui-material-rim-color)) border-box,
86
87
  var(--_ui-material-content-color) padding-box;
@@ -96,6 +97,7 @@
96
97
  0 calc(var(--_ui-material-separation) * var(--_ui-material-scale, 1) * 8px) calc(
97
98
  var(--_ui-material-separation) * var(--_ui-material-scale, 1) * 24px
98
99
  ) color-mix(in oklab, var(--ui-text-primary) 15%, transparent),
100
+ var(--ui-material-glow, 0 0 transparent),
99
101
  inset 0 0 0 calc(var(--_ui-material-active) * var(--ui-border-width)) var(--ui-material-edge, var(--ui-border-strong)),
100
102
  var(--ui-elevation-dialog, var(--ui-shadow-xl))
101
103
  )
@@ -400,6 +402,7 @@
400
402
  --_ui-material-content-color: var(--ui-dialog-surface, var(--ui-material-content, var(--ui-overlay-surface, var(--ui-surface-elevated))));
401
403
  --_ui-material-rim-color: color-mix(in srgb, var(--ui-material-glass, var(--ui-surface-elevated)) calc(var(--_ui-material-active) * 100%), transparent);
402
404
  background:
405
+ var(--ui-surface-highlight, linear-gradient(transparent, transparent)) padding-box,
403
406
  linear-gradient(var(--_ui-material-content-color), var(--_ui-material-content-color)) padding-box,
404
407
  linear-gradient(var(--_ui-material-rim-color), var(--_ui-material-rim-color)) border-box,
405
408
  var(--_ui-material-content-color) padding-box;
@@ -415,6 +418,7 @@
415
418
  0 calc(var(--_ui-material-separation) * var(--_ui-material-scale, 1) * 8px) calc(
416
419
  var(--_ui-material-separation) * var(--_ui-material-scale, 1) * 24px
417
420
  ) color-mix(in oklab, var(--ui-text-primary) 15%, transparent),
421
+ var(--ui-material-glow, 0 0 transparent),
418
422
  inset 0 0 0 calc(var(--_ui-material-active) * var(--ui-border-width)) var(--ui-material-edge, var(--ui-border-strong)),
419
423
  var(--ui-elevation-dialog, var(--ui-shadow-xl))
420
424
  )
@@ -88,7 +88,9 @@
88
88
  :scope:where([data-component~="ui-icon-button"]) {
89
89
  /* Optional inherited action-group values keep text and icon actions under the same light.
90
90
  One fine top/start highlight follows the bottom/end lowlight at a joined action seam.
91
- A single ambient lift avoids stacking a face gradient or another inset bevel. */
91
+ The shared theme supplies reflections and glow independently of the resting lift. */
92
+ --_tone-highlight: var(--ui-accent-highlight, none);
93
+ --_tone-glow: var(--ui-accent-glow, 0 0 transparent);
92
94
  --_ui-action-start: 1px;
93
95
  --_ui-action-edge: var(
94
96
  --ui-action-edge,
@@ -109,6 +111,8 @@
109
111
  }
110
112
 
111
113
  :scope:where([data-component~="ui-icon-button"])[data-tone="accent"] {
114
+ --_tone-highlight: var(--ui-accent-highlight, none);
115
+ --_tone-glow: var(--ui-accent-glow, 0 0 transparent);
112
116
  --_icon-button-border: var(--ui-accent-solid);
113
117
  --_icon-button-hover-border: var(--ui-accent-hover);
114
118
  --_icon-button-text: var(--ui-accent-subtle-text, var(--ui-accent-solid));
@@ -117,6 +121,8 @@
117
121
  }
118
122
 
119
123
  :scope:where([data-component~="ui-icon-button"])[data-tone="danger"] {
124
+ --_tone-highlight: var(--ui-danger-highlight, none);
125
+ --_tone-glow: var(--ui-danger-glow, 0 0 transparent);
120
126
  --_icon-button-border: var(--ui-danger);
121
127
  --_icon-button-hover-border: var(--ui-danger-hover);
122
128
  --_icon-button-text: var(--ui-danger-subtle-text);
@@ -128,6 +134,8 @@
128
134
  }
129
135
 
130
136
  :scope:where([data-component~="ui-icon-button"])[data-tone="success"] {
137
+ --_tone-highlight: var(--ui-success-highlight, none);
138
+ --_tone-glow: var(--ui-success-glow, 0 0 transparent);
131
139
  --_icon-button-border: var(--ui-success);
132
140
  --_icon-button-hover-border: var(--ui-success-hover);
133
141
  --_icon-button-text: var(--ui-success-subtle-text);
@@ -139,6 +147,8 @@
139
147
  }
140
148
 
141
149
  :scope:where([data-component~="ui-icon-button"])[data-tone="warning"] {
150
+ --_tone-highlight: var(--ui-warning-highlight, none);
151
+ --_tone-glow: var(--ui-warning-glow, 0 0 transparent);
142
152
  --_icon-button-border: var(--ui-warning);
143
153
  --_icon-button-hover-border: var(--ui-warning-hover);
144
154
  --_icon-button-text: var(--ui-warning-subtle-text);
@@ -150,6 +160,8 @@
150
160
  }
151
161
 
152
162
  :scope:where([data-component~="ui-icon-button"])[data-tone="info"] {
163
+ --_tone-highlight: var(--ui-info-highlight, none);
164
+ --_tone-glow: var(--ui-info-glow, 0 0 transparent);
153
165
  --_icon-button-border: var(--ui-info);
154
166
  --_icon-button-hover-border: var(--ui-info-hover);
155
167
  --_icon-button-text: var(--ui-info-subtle-text);
@@ -162,9 +174,11 @@
162
174
 
163
175
  /* Omitted tone retains the existing accent solid; explicit neutral is additive. */
164
176
  :scope:where([data-component~="ui-icon-button"])[data-tone="neutral"] {
165
- --_icon-button-solid-surface: var(--ui-text-primary);
166
- --_icon-button-solid-hover-surface: var(--ui-text-primary);
167
- --_icon-button-solid-text: var(--ui-surface-default);
177
+ --_tone-highlight: var(--ui-neutral-highlight, none);
178
+ --_tone-glow: var(--ui-neutral-glow, 0 0 transparent);
179
+ --_icon-button-solid-surface: var(--ui-neutral-surface);
180
+ --_icon-button-solid-hover-surface: var(--ui-neutral-hover);
181
+ --_icon-button-solid-text: var(--ui-text);
168
182
  }
169
183
 
170
184
  :scope:where([data-component~="ui-icon-button"])[data-ui-icon-button-state~="anticipatory"] {
@@ -332,16 +346,16 @@
332
346
  :scope:where([data-component~="ui-icon-button"])[data-appearance="outline"] {
333
347
  border-color: var(--ui-icon-button-border, var(--_icon-button-border, var(--ui-control-border, var(--ui-border-strong))));
334
348
  background-color: var(--ui-icon-button-outline-surface, var(--_icon-button-outline-surface, var(--ui-surface-default)));
335
- background-image: none;
349
+ background-image: var(--ui-surface-highlight, none);
336
350
  color: var(--ui-icon-button-outline-text, var(--_icon-button-text, var(--ui-text-primary)));
337
- box-shadow: var(--ui-action-shadow, var(--_ui-action-shadow));
351
+ box-shadow: var(--ui-action-shadow, var(--_ui-action-shadow)), var(--_tone-glow);
338
352
  }
339
353
 
340
354
  :scope:where([data-component~="ui-icon-button"])[data-appearance="solid"] {
341
355
  background-color: var(--ui-icon-button-solid-surface, var(--_icon-button-solid-surface, var(--ui-accent-solid)));
342
- background-image: none;
356
+ background-image: var(--_tone-highlight);
343
357
  color: var(--ui-icon-button-solid-text, var(--_icon-button-solid-text, var(--ui-text-on-accent)));
344
- box-shadow: var(--ui-icon-button-solid-shadow, var(--ui-action-shadow, var(--_ui-action-shadow)));
358
+ box-shadow: var(--ui-icon-button-solid-shadow, var(--ui-action-shadow, var(--_ui-action-shadow))), var(--_tone-glow);
345
359
  }
346
360
 
347
361
  :scope:where([data-component~="ui-icon-button"]):hover:enabled:not([aria-disabled="true"]) {
@@ -42,6 +42,7 @@
42
42
  --_ui-default-input-focus-outline: none;
43
43
  --_ui-default-input-radius: 0;
44
44
  --_ui-default-input-surface: transparent;
45
+ --_ui-default-input-highlight: none;
45
46
  --_ui-default-textarea-border-width: 0;
46
47
  --_ui-default-textarea-border: transparent;
47
48
  --_ui-default-textarea-border-hover: transparent;
@@ -53,6 +54,7 @@
53
54
  --_ui-default-textarea-focus-outline: none;
54
55
  --_ui-default-textarea-radius: 0;
55
56
  --_ui-default-textarea-surface: transparent;
57
+ --_ui-default-textarea-highlight: none;
56
58
 
57
59
  display: flex !important;
58
60
  align-items: stretch !important;
@@ -63,7 +65,8 @@
63
65
  /* The frame reads the shared control tokens with a lone Input's fallbacks, so the two match. */
64
66
  border: var(--ui-border-width) solid var(--ui-control-border, var(--ui-border-strong));
65
67
  border-radius: var(--ui-input-group-radius, var(--ui-field-radius, var(--ui-radius-md)));
66
- background: var(--ui-control-surface, var(--ui-surface-elevated));
68
+ background-color: var(--ui-control-surface, var(--ui-surface-elevated));
69
+ background-image: var(--ui-surface-highlight, none);
67
70
  box-shadow: var(--ui-control-inset);
68
71
  overflow: hidden;
69
72
  /* A press anywhere in the frame focuses the input. */
@@ -75,7 +75,8 @@
75
75
  border: var(--ui-input-border-width, var(--_ui-default-input-border-width, var(--ui-border-width))) solid
76
76
  var(--ui-input-border, var(--_ui-default-input-border, var(--ui-control-border, var(--ui-border-strong))));
77
77
  border-radius: var(--ui-input-radius, var(--_ui-default-input-radius, var(--ui-field-radius, var(--ui-radius-md))));
78
- background: var(--ui-input-surface, var(--_ui-default-input-surface, var(--ui-control-surface, var(--ui-surface-elevated))));
78
+ background-color: var(--ui-input-surface, var(--_ui-default-input-surface, var(--ui-control-surface, var(--ui-surface-elevated))));
79
+ background-image: var(--_ui-default-input-highlight, var(--ui-surface-highlight, none));
79
80
  color: var(--ui-text-primary);
80
81
  font: inherit;
81
82
  line-height: var(--ui-line-height-tight);
@@ -0,0 +1,28 @@
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-light-editor"] { --html-next-scope-owner: "ui-light-editor" !important; }
8
+ }
9
+ @scope ([data-component~="ui-light-editor"]) to ([data-component], [data-slotted]) {
10
+ :scope:where([data-component~="ui-light-editor"]) { display: grid !important; gap: var(--ui-space-3) !important; min-inline-size: 0; padding: 0 !important; margin: 0 !important; }
11
+ .stage { box-sizing: border-box !important; display: block !important; block-size: 13rem; padding: calc(var(--ui-control-min-block-size) / 2) !important; border: var(--ui-border-width) solid var(--ui-control-border); border-radius: var(--ui-field-radius, var(--ui-radius-md)); background: var(--ui-neutral-surface, var(--ui-surface)); }
12
+ .plane { position: relative; display: block !important; inline-size: 100%; block-size: 100%; }
13
+ svg { inline-size: 100%; block-size: 100%; overflow: visible; }
14
+ path { fill: none; stroke: var(--ui-border); stroke-width: 1; vector-effect: non-scaling-stroke; }
15
+ .specimen { position: absolute; inset: 0; display: grid !important; place-items: center !important; }
16
+ .source { position: absolute; left: 50%; top: 0; translate: -50% -50%; display: grid !important; place-items: center !important; inline-size: max(2rem, var(--ui-control-min-block-size)); block-size: max(2rem, var(--ui-control-min-block-size)); padding: 0 !important; margin: 0 !important; border: 0; border-radius: 50%; background: transparent; cursor: grab; touch-action: none; }
17
+ .source:where(:not([data-component], [data-slotted]))::after { content: ""; inline-size: 1rem; block-size: 1rem; border: var(--ui-border-width) solid var(--ui-accent); border-radius: 50%; background: var(--ui-surface-raised); box-shadow: 0 0 0 var(--ui-space-2) color-mix(in srgb, var(--ui-accent) 12%, transparent), var(--ui-shadow-sm); }
18
+ .source:active { cursor: grabbing; }
19
+ .source:focus-visible { outline: var(--ui-focus-width) solid var(--ui-focus-ring); outline-offset: var(--ui-space-1); }
20
+ .source:disabled { cursor: not-allowed; }
21
+ .source:disabled:where(:not([data-component], [data-slotted]))::after { background: var(--ui-disabled-surface); border-color: var(--ui-disabled-border); box-shadow: none; }
22
+ .coordinates { display: grid !important; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--ui-space-2) !important; }
23
+ .hint { font-size: var(--ui-font-size-sm); color: var(--ui-text-secondary); }
24
+ @media (forced-colors: active) { .source:where(:not([data-component], [data-slotted]))::after { background: Canvas; border-color: CanvasText; } .source:focus-visible { outline-color: Highlight; } }
25
+ }
26
+ @scope ([data-component~="ui-light-editor"]) to ([data-component]) {
27
+ @media (forced-colors: active) { }
28
+ }
@@ -110,6 +110,7 @@
110
110
  --_ui-material-content-color: var(--_ui-menu-surface, var(--ui-material-content, var(--ui-overlay-surface, var(--ui-surface-elevated))));
111
111
  --_ui-material-rim-color: color-mix(in srgb, var(--ui-material-glass, var(--ui-surface-elevated)) calc(var(--_ui-material-active) * 100%), transparent);
112
112
  background:
113
+ var(--ui-surface-highlight, linear-gradient(transparent, transparent)) padding-box,
113
114
  linear-gradient(var(--_ui-material-content-color), var(--_ui-material-content-color)) padding-box,
114
115
  linear-gradient(var(--_ui-material-rim-color), var(--_ui-material-rim-color)) border-box,
115
116
  var(--_ui-material-content-color) padding-box;
@@ -125,6 +126,7 @@
125
126
  0 calc(var(--_ui-material-separation) * var(--_ui-material-scale, 0.28) * 8px) calc(
126
127
  var(--_ui-material-separation) * var(--_ui-material-scale, 0.28) * 24px
127
128
  ) color-mix(in oklab, var(--ui-text-primary) 15%, transparent),
129
+ var(--ui-material-glow, 0 0 transparent),
128
130
  inset 0 0 0 calc(var(--_ui-material-active) * var(--ui-border-width)) var(--ui-material-edge, var(--ui-border-strong)),
129
131
  var(--ui-elevation-overlay, var(--ui-shadow-lg))
130
132
  )
@@ -81,6 +81,7 @@
81
81
  --_ui-material-content-color: var(--_ui-popover-surface, var(--ui-material-content, var(--ui-overlay-surface, var(--ui-surface-elevated))));
82
82
  --_ui-material-rim-color: color-mix(in srgb, var(--ui-material-glass, var(--ui-surface-elevated)) calc(var(--_ui-material-active) * 100%), transparent);
83
83
  background:
84
+ var(--ui-surface-highlight, linear-gradient(transparent, transparent)) padding-box,
84
85
  linear-gradient(var(--_ui-material-content-color), var(--_ui-material-content-color)) padding-box,
85
86
  linear-gradient(var(--_ui-material-rim-color), var(--_ui-material-rim-color)) border-box,
86
87
  var(--_ui-material-content-color) padding-box;
@@ -96,6 +97,7 @@
96
97
  0 calc(var(--_ui-material-separation) * var(--_ui-material-scale, 0.55) * 8px) calc(
97
98
  var(--_ui-material-separation) * var(--_ui-material-scale, 0.55) * 24px
98
99
  ) color-mix(in oklab, var(--ui-text-primary) 15%, transparent),
100
+ var(--ui-material-glow, 0 0 transparent),
99
101
  inset 0 0 0 calc(var(--_ui-material-active) * var(--ui-border-width)) var(--ui-material-edge, var(--ui-border-strong)),
100
102
  var(--ui-elevation-overlay, var(--ui-shadow-lg))
101
103
  )