@threadlabs/looma 0.34.0 → 0.35.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 (88) 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-select/ui-select.html +9 -7
  16. package/components/ui-switch/ui-switch.html +3 -0
  17. package/components/ui-textarea/ui-textarea.html +2 -1
  18. package/dist/index.js +24 -23
  19. package/docs/README.md +26 -2
  20. package/docs/catalog.json +566 -44
  21. package/docs/customizer-render.mjs +24 -11
  22. package/docs/customizer.js +67 -17
  23. package/docs/index.d.ts +4 -1
  24. package/docs/index.mjs +1 -1
  25. package/docs/render.mjs +1 -1
  26. package/docs/site.mjs +30 -20
  27. package/docs/theme-session.mjs +4 -2
  28. package/docs/theme.mjs +59 -7
  29. package/package.json +2 -2
  30. package/styles/ui-badge.css +11 -6
  31. package/styles/ui-button.css +29 -4
  32. package/styles/ui-card.css +9 -9
  33. package/styles/ui-checkbox.css +4 -2
  34. package/styles/ui-dialog.css +4 -0
  35. package/styles/ui-icon-button.css +22 -8
  36. package/styles/ui-input-group.css +4 -1
  37. package/styles/ui-input.css +2 -1
  38. package/styles/ui-light-editor.css +28 -0
  39. package/styles/ui-menu.css +2 -0
  40. package/styles/ui-popover.css +2 -0
  41. package/styles/ui-select.css +9 -7
  42. package/styles/ui-switch.css +3 -0
  43. package/styles/ui-textarea.css +2 -1
  44. package/theme-light.css +1 -0
  45. package/theme-system.css +43 -7
  46. package/tokens.css +4 -0
  47. package/vanilla/UiCard.d.ts +2 -1
  48. package/vanilla/UiCard.js +12 -12
  49. package/vanilla/UiLightEditor.d.ts +29 -0
  50. package/vanilla/UiLightEditor.js +102 -0
  51. package/vanilla/UiNumberInput.js +27 -20
  52. package/vanilla/index.js +1 -0
  53. package/vue/UiBadge.vue +29 -4
  54. package/vue/UiButton.vue +33 -4
  55. package/vue/UiCard.d.ts +8 -2
  56. package/vue/UiCard.vue +41 -17
  57. package/vue/UiCheckbox.vue +4 -2
  58. package/vue/UiDialog.vue +4 -0
  59. package/vue/UiIconButton.vue +26 -11
  60. package/vue/UiInput.vue +2 -1
  61. package/vue/UiInputGroup.vue +4 -1
  62. package/vue/UiLightEditor.d.ts +75 -0
  63. package/vue/UiLightEditor.js +2 -0
  64. package/vue/UiLightEditor.vue +402 -0
  65. package/vue/UiMenu.vue +2 -0
  66. package/vue/UiNumberInput.vue +32 -3
  67. package/vue/UiPopover.vue +2 -0
  68. package/vue/UiSelect.vue +16 -10
  69. package/vue/UiSwitch.vue +3 -0
  70. package/vue/UiTextarea.vue +2 -1
  71. package/vue/chunks/UiBadge.js +1 -1
  72. package/vue/chunks/UiButton.js +1 -1
  73. package/vue/chunks/UiCard.js +33 -5
  74. package/vue/chunks/UiCheckbox.js +1 -1
  75. package/vue/chunks/UiDialog.js +1 -1
  76. package/vue/chunks/UiIconButton.js +1 -1
  77. package/vue/chunks/UiInput.js +1 -1
  78. package/vue/chunks/UiInputGroup.js +1 -1
  79. package/vue/chunks/UiLightEditor.js +347 -0
  80. package/vue/chunks/UiMenu.js +1 -1
  81. package/vue/chunks/UiNumberInput.js +21 -8
  82. package/vue/chunks/UiPopover.js +1 -1
  83. package/vue/chunks/UiSelect.js +1 -1
  84. package/vue/chunks/UiSwitch.js +1 -1
  85. package/vue/chunks/UiTextarea.js +1 -1
  86. package/vue/components.css +306 -66
  87. package/vue/index.d.ts +1 -0
  88. package/vue/index.js +2 -1
@@ -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
  )
@@ -61,7 +61,7 @@
61
61
  border-radius: var(--ui-select-radius, var(--ui-field-radius, var(--ui-radius-md)));
62
62
  background-color: var(--ui-select-surface, var(--ui-control-surface, var(--ui-surface-elevated)));
63
63
  color: var(--ui-text-primary);
64
- color-scheme: light dark;
64
+ color-scheme: inherit;
65
65
  font: inherit;
66
66
  line-height: var(--ui-line-height-tight);
67
67
  box-shadow: var(--ui-select-shadow, var(--ui-shadow-sm));
@@ -74,15 +74,16 @@
74
74
  /* The select draws its own chevron: the native caret cannot be inset or sized consistently.
75
75
  * The root stays a native <s-lect>, so the icon is a background image rather than an element. */
76
76
  :scope:where([data-component~="ui-select"]) {
77
+ --_ui-select-highlight: var(--ui-surface-highlight, linear-gradient(transparent, transparent));
77
78
  appearance: none;
78
79
  padding-inline-end: calc(var(--ui-space-3) + 1.5rem) !important;
79
80
  background-image: var(
80
81
  --ui-select-chevron,
81
82
  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235c5c58' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")
82
- );
83
+ ), var(--_ui-select-highlight);
83
84
  background-repeat: no-repeat;
84
- background-position: right var(--ui-space-3) center;
85
- background-size: 1.125rem 1.125rem;
85
+ background-position: right var(--ui-space-3) center, center;
86
+ background-size: 1.125rem 1.125rem, 100% 100%;
86
87
  }
87
88
 
88
89
  :scope:where([data-component~="ui-select"]):hover:not(:focus):not(:disabled):not([aria-invalid="true"]):not(:is(:user-invalid, [data-user-invalid])) {
@@ -114,6 +115,7 @@
114
115
  }
115
116
 
116
117
  :scope:where([data-component~="ui-select"]):disabled {
118
+ --_ui-select-highlight: none;
117
119
  background-color: var(--ui-control-surface-disabled, var(--ui-surface-muted));
118
120
  color: var(--ui-disabled-text);
119
121
  box-shadow: none;
@@ -124,8 +126,8 @@
124
126
  min-block-size: var(--ui-control-size-sm);
125
127
  padding: var(--ui-control-inset-block-sm, var(--ui-space-1)) var(--ui-space-2) !important;
126
128
  padding-inline-end: calc(var(--ui-space-2) + var(--ui-icon-size-sm) + var(--ui-space-1)) !important;
127
- background-position: right var(--ui-space-2) center;
128
- background-size: var(--ui-icon-size-sm) var(--ui-icon-size-sm);
129
+ background-position: right var(--ui-space-2) center, center;
130
+ background-size: var(--ui-icon-size-sm) var(--ui-icon-size-sm), 100% 100%;
129
131
  font-size: var(--ui-font-size-sm);
130
132
  }
131
133
 
@@ -133,7 +135,7 @@
133
135
  min-block-size: var(--ui-control-size-lg);
134
136
  padding: var(--ui-control-inset-block-lg, var(--ui-space-3)) var(--ui-space-4) !important;
135
137
  padding-inline-end: calc(var(--ui-space-4) + 1.5rem) !important;
136
- background-position: right var(--ui-space-4) center;
138
+ background-position: right var(--ui-space-4) center, center;
137
139
  font-size: var(--ui-font-size-md);
138
140
  }
139
141
 
@@ -84,6 +84,7 @@
84
84
  border: var(--ui-border-width) solid var(--ui-control-border, var(--ui-border-strong));
85
85
  border-radius: var(--ui-radius-round);
86
86
  background-color: var(--ui-control-surface, var(--ui-surface-default));
87
+ background-image: var(--ui-surface-highlight, none);
87
88
  box-shadow: var(--ui-control-inset);
88
89
  cursor: pointer;
89
90
  transition: background-color var(--ui-motion-base) var(--ui-motion-ease), border-color var(--ui-motion-base) var(--ui-motion-ease);
@@ -105,6 +106,7 @@
105
106
 
106
107
  input:checked {
107
108
  background-color: var(--ui-accent-solid);
109
+ background-image: var(--ui-accent-highlight, none);
108
110
  border-color: var(--ui-accent-solid);
109
111
  }
110
112
 
@@ -128,6 +130,7 @@
128
130
  :scope:where([data-component~="ui-switch"])[data-ui-switch-state~="disabled"] input {
129
131
  border-color: var(--ui-disabled-border);
130
132
  background-color: var(--ui-disabled-surface);
133
+ background-image: none;
131
134
  box-shadow: none;
132
135
  cursor: not-allowed;
133
136
  }
@@ -72,7 +72,8 @@
72
72
  border: var(--ui-textarea-border-width, var(--_ui-default-textarea-border-width, var(--ui-border-width))) solid
73
73
  var(--ui-textarea-border, var(--_ui-default-textarea-border, var(--ui-control-border, var(--ui-border-strong))));
74
74
  border-radius: var(--ui-textarea-radius, var(--_ui-default-textarea-radius, var(--ui-field-radius, var(--ui-radius-md))));
75
- background: var(--ui-textarea-surface, var(--_ui-default-textarea-surface, var(--ui-control-surface, var(--ui-surface-elevated))));
75
+ background-color: var(--ui-textarea-surface, var(--_ui-default-textarea-surface, var(--ui-control-surface, var(--ui-surface-elevated))));
76
+ background-image: var(--_ui-default-textarea-highlight, var(--ui-surface-highlight, none));
76
77
  color: var(--ui-text-primary);
77
78
  font: inherit;
78
79
  line-height: var(--ui-line-height-md);
package/theme-light.css CHANGED
@@ -2,6 +2,7 @@
2
2
  :root:not([data-theme], [data-contrast="high"]),
3
3
  [data-theme="light"] {
4
4
  color-scheme: light;
5
+ --ui-select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235c5c58' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
5
6
  /* The seeds, and only the seeds. Neutrals, hovers, tints, and disabled colours are mixed from
6
7
  these in tokens.css, so a product themes Looma by changing these eleven values. */
7
8
  --ui-surface: #ffffff;
package/theme-system.css CHANGED
@@ -2,6 +2,9 @@
2
2
  Existing public roles remain the customization interface; no component selectors. */
3
3
  /* Resolve shared geometry once where it is declared, rather than once per consuming
4
4
  control's font. Palette roles remain unregistered so light-dark resolves locally. */
5
+ /* Reserve the same order when the remembered theme arrives before baseline CSS. */
6
+ @layer tokens, base, theme, components;
7
+
5
8
  @property --ui-font-size { syntax: "<length>"; inherits: true; initial-value: 16px; }
6
9
  @property --ui-control-size { syntax: "<length>"; inherits: true; initial-value: 40px; }
7
10
  @property --ui-radius-md { syntax: "<length>"; inherits: true; initial-value: 8px; }
@@ -18,6 +21,12 @@
18
21
  --ui-overlay-translucency: 0;
19
22
  --ui-glassiness: 0;
20
23
  --ui-surface-separation: 0;
24
+ --ui-finish: 0;
25
+ --ui-glow: 0;
26
+ --ui-light-x: 0.5;
27
+ --ui-light-y: 0;
28
+ --ui-light-spread: 0.5;
29
+ --ui-light-strength: 0.65;
21
30
  }
22
31
 
23
32
  :where(:root, [data-theme], [data-contrast]) {
@@ -58,26 +67,47 @@
58
67
  --_ui-theme-outer: max(0, var(--ui-outer-depth));
59
68
  --_ui-theme-inner: max(0, var(--ui-inner-depth));
60
69
  --_ui-theme-edge: clamp(0, var(--ui-edge-light), 1);
61
- --_ui-theme-edge-paint: inset 1px 1px 0 rgb(255 255 255 / calc(var(--_ui-theme-edge) * 0.08)), inset -1px -1px 0 rgb(0 0 0 / calc(var(--_ui-theme-edge) * 0.08));
70
+ --_ui-light-inline: calc((0.5 - var(--ui-light-x)) * 2px);
71
+ --_ui-shadow-x: calc((0.5 - var(--ui-light-x)) * 2);
72
+ --_ui-shadow-y: calc((0.5 - var(--ui-light-y)) * 2);
73
+ --_ui-light-block: calc((0.5 - var(--ui-light-y)) * 2px);
74
+ --_ui-theme-edge-paint: inset var(--_ui-light-inline) var(--_ui-light-block) 0 rgb(255 255 255 / calc(var(--_ui-theme-edge) * var(--ui-light-strength) * 0.3)), inset calc(-1 * var(--_ui-light-inline)) calc(-1 * var(--_ui-light-block)) 0 rgb(0 0 0 / calc(var(--_ui-theme-edge) * 0.1));
62
75
  --ui-action-edge: var(--_ui-theme-edge-paint);
63
- --ui-shadow-xs: 0 calc(1px * var(--_ui-theme-outer)) calc(2px * var(--_ui-theme-outer)) rgb(0 0 0 / 0.08);
64
- --ui-shadow-sm: 0 calc(1px * var(--_ui-theme-outer)) calc(3px * var(--_ui-theme-outer)) rgb(0 0 0 / 0.12), 0 calc(4px * var(--_ui-theme-outer)) calc(12px * var(--_ui-theme-outer)) rgb(0 0 0 / 0.06);
76
+ --ui-shadow-xs: calc(1px * var(--_ui-theme-outer) * var(--_ui-shadow-x)) calc(1px * var(--_ui-theme-outer) * var(--_ui-shadow-y)) calc(2px * var(--_ui-theme-outer)) rgb(0 0 0 / 0.08);
77
+ --ui-shadow-sm: calc(1px * var(--_ui-theme-outer) * var(--_ui-shadow-x)) calc(1px * var(--_ui-theme-outer) * var(--_ui-shadow-y)) calc(3px * var(--_ui-theme-outer)) rgb(0 0 0 / 0.12), calc(4px * var(--_ui-theme-outer) * var(--_ui-shadow-x)) calc(4px * var(--_ui-theme-outer) * var(--_ui-shadow-y)) calc(12px * var(--_ui-theme-outer)) rgb(0 0 0 / 0.06);
65
78
  --ui-shadow-md: var(--ui-shadow-sm);
66
- --ui-shadow-lg: 0 calc(8px * var(--_ui-theme-outer)) calc(24px * var(--_ui-theme-outer)) rgb(0 0 0 / 0.12), 0 calc(4px * var(--_ui-theme-outer)) calc(8px * var(--_ui-theme-outer)) rgb(0 0 0 / 0.06);
79
+ --ui-shadow-lg: calc(8px * var(--_ui-theme-outer) * var(--_ui-shadow-x)) calc(8px * var(--_ui-theme-outer) * var(--_ui-shadow-y)) calc(24px * var(--_ui-theme-outer)) rgb(0 0 0 / 0.12), calc(4px * var(--_ui-theme-outer) * var(--_ui-shadow-x)) calc(4px * var(--_ui-theme-outer) * var(--_ui-shadow-y)) calc(8px * var(--_ui-theme-outer)) rgb(0 0 0 / 0.06);
67
80
  --ui-shadow-xl: var(--ui-shadow-lg);
68
- --ui-action-shadow: var(--ui-shadow-sm);
81
+ --ui-action-shadow: var(--ui-action-edge), var(--ui-shadow-sm);
69
82
  --ui-raised: var(--ui-shadow-sm);
70
83
  --ui-elevation-tooltip: var(--ui-shadow-sm);
71
84
  --ui-elevation-overlay: var(--ui-shadow-lg);
72
85
  --ui-elevation-dialog: var(--ui-shadow-lg);
73
- --ui-control-inset: inset 0 calc(1px * var(--_ui-theme-inner)) calc(3px * var(--_ui-theme-inner)) rgb(0 0 0 / 0.15), inset 0 -1px 0 rgb(255 255 255 / calc(var(--_ui-theme-edge) * 0.18));
74
- --ui-pressed: inset 0 calc(2px * var(--_ui-theme-inner)) calc(4px * var(--_ui-theme-inner)) rgb(0 0 0 / 0.2);
86
+ --ui-control-inset: inset calc(var(--_ui-light-inline) * var(--_ui-theme-inner)) calc(var(--_ui-light-block) * var(--_ui-theme-inner)) calc(3px * var(--_ui-theme-inner)) rgb(0 0 0 / 0.15), inset calc(-1 * var(--_ui-light-inline)) calc(-1 * var(--_ui-light-block)) 0 rgb(255 255 255 / calc(var(--_ui-theme-edge) * var(--ui-light-strength) * 0.18)), var(--ui-material-glow);
87
+ --ui-pressed: inset calc(2 * var(--_ui-light-inline) * var(--_ui-theme-inner)) calc(2 * var(--_ui-light-block) * var(--_ui-theme-inner)) calc(4px * var(--_ui-theme-inner)) rgb(0 0 0 / 0.2);
75
88
  --ui-overlay-surface: color-mix(in srgb, var(--ui-surface-raised) calc((1 - clamp(0, var(--ui-overlay-translucency), 1)) * 100%), transparent);
76
89
  /* Decorative outer material is allowed to transmit the scene. Content keeps
77
90
  its own opaque plane; changing the scene cannot undermine text contrast. */
78
91
  --ui-material-content: var(--ui-surface-raised);
79
92
  --ui-material-glass: color-mix(in srgb, var(--ui-surface-raised) calc((1 - clamp(0, var(--ui-overlay-translucency), 1)) * 35%), transparent);
80
93
  --ui-material-edge: color-mix(in srgb, var(--ui-surface-raised) 80%, var(--ui-text));
94
+ /* Each role has a bounded reflection colour. Shared source coordinates are
95
+ relative to each surface, so size and position never change the lighting. */
96
+ --ui-surface-highlight: radial-gradient(ellipse calc(35% + var(--ui-light-spread) * 165%) calc(55% + var(--ui-light-spread) * 200%) at calc(var(--ui-light-x) * 100%) calc(var(--ui-light-y) * 100%), color-mix(in srgb, var(--ui-surface-reflection, var(--ui-surface-raised)) calc(var(--ui-finish) * var(--ui-light-strength) * 100%), transparent), transparent 85%);
97
+ --ui-neutral-highlight: radial-gradient(ellipse calc(35% + var(--ui-light-spread) * 165%) calc(55% + var(--ui-light-spread) * 200%) at calc(var(--ui-light-x) * 100%) calc(var(--ui-light-y) * 100%), color-mix(in srgb, var(--ui-neutral-reflection, var(--ui-surface-raised)) calc(var(--ui-finish) * var(--ui-light-strength) * 100%), transparent), transparent 85%);
98
+ --ui-accent-highlight: radial-gradient(ellipse calc(35% + var(--ui-light-spread) * 165%) calc(55% + var(--ui-light-spread) * 200%) at calc(var(--ui-light-x) * 100%) calc(var(--ui-light-y) * 100%), color-mix(in srgb, var(--ui-accent-reflection, var(--ui-surface-raised)) calc(var(--ui-finish) * var(--ui-light-strength) * 100%), transparent), transparent 85%);
99
+ --ui-danger-highlight: radial-gradient(ellipse calc(35% + var(--ui-light-spread) * 165%) calc(55% + var(--ui-light-spread) * 200%) at calc(var(--ui-light-x) * 100%) calc(var(--ui-light-y) * 100%), color-mix(in srgb, var(--ui-danger-reflection, var(--ui-surface-raised)) calc(var(--ui-finish) * var(--ui-light-strength) * 100%), transparent), transparent 85%);
100
+ --ui-success-highlight: radial-gradient(ellipse calc(35% + var(--ui-light-spread) * 165%) calc(55% + var(--ui-light-spread) * 200%) at calc(var(--ui-light-x) * 100%) calc(var(--ui-light-y) * 100%), color-mix(in srgb, var(--ui-success-reflection, var(--ui-surface-raised)) calc(var(--ui-finish) * var(--ui-light-strength) * 100%), transparent), transparent 85%);
101
+ --ui-warning-highlight: radial-gradient(ellipse calc(35% + var(--ui-light-spread) * 165%) calc(55% + var(--ui-light-spread) * 200%) at calc(var(--ui-light-x) * 100%) calc(var(--ui-light-y) * 100%), color-mix(in srgb, var(--ui-warning-reflection, var(--ui-surface-raised)) calc(var(--ui-finish) * var(--ui-light-strength) * 100%), transparent), transparent 85%);
102
+ --ui-info-highlight: radial-gradient(ellipse calc(35% + var(--ui-light-spread) * 165%) calc(55% + var(--ui-light-spread) * 200%) at calc(var(--ui-light-x) * 100%) calc(var(--ui-light-y) * 100%), color-mix(in srgb, var(--ui-info-reflection, var(--ui-surface-raised)) calc(var(--ui-finish) * var(--ui-light-strength) * 100%), transparent), transparent 85%);
103
+ --ui-material-glow: 0 0 calc(18px * var(--ui-glow)) color-mix(in srgb, var(--ui-accent) calc(var(--ui-glow) * var(--ui-light-strength) * 18%), transparent);
104
+ --ui-neutral-glow: 0 0 calc(18px * var(--ui-glow)) color-mix(in srgb, var(--ui-text) calc(var(--ui-glow) * var(--ui-light-strength) * 18%), transparent);
105
+ --ui-accent-glow: 0 0 calc(18px * var(--ui-glow)) color-mix(in srgb, var(--ui-accent) calc(var(--ui-glow) * var(--ui-light-strength) * 18%), transparent);
106
+ --ui-danger-glow: 0 0 calc(18px * var(--ui-glow)) color-mix(in srgb, var(--ui-danger) calc(var(--ui-glow) * var(--ui-light-strength) * 18%), transparent);
107
+ --ui-success-glow: 0 0 calc(18px * var(--ui-glow)) color-mix(in srgb, var(--ui-success) calc(var(--ui-glow) * var(--ui-light-strength) * 18%), transparent);
108
+ --ui-warning-glow: 0 0 calc(18px * var(--ui-glow)) color-mix(in srgb, var(--ui-warning) calc(var(--ui-glow) * var(--ui-light-strength) * 18%), transparent);
109
+ --ui-info-glow: 0 0 calc(18px * var(--ui-glow)) color-mix(in srgb, var(--ui-info) calc(var(--ui-glow) * var(--ui-light-strength) * 18%), transparent);
110
+ --ui-raised-highlight: var(--ui-surface-highlight);
81
111
 
82
112
  /* Existing timing and easing names stay canonical. */
83
113
  --ui-motion-press: var(--ui-motion-fast);
@@ -93,4 +123,10 @@
93
123
  --ui-press-scale: 1;
94
124
  }
95
125
  }
126
+ @media (prefers-reduced-transparency: reduce), (forced-colors: active) {
127
+ :where(:root, [data-theme], [data-contrast]) {
128
+ --ui-finish: 0;
129
+ --ui-glow: 0;
130
+ }
131
+ }
96
132
  }