wave-ui 3.28.0 → 4.0.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 (89) hide show
  1. package/dist/types/types/$waveui.d.ts +15 -1
  2. package/dist/types/types/colors.d.ts +2 -0
  3. package/dist/types/types/components/WAccordion.d.ts +92 -6
  4. package/dist/types/types/components/WAutocomplete.d.ts +437 -0
  5. package/dist/types/types/components/WCheckbox.d.ts +34 -0
  6. package/dist/types/types/components/WCheckboxes.d.ts +30 -0
  7. package/dist/types/types/components/WInput.d.ts +34 -0
  8. package/dist/types/types/components/WMenu.d.ts +12 -6
  9. package/dist/types/types/components/WRadio.d.ts +34 -0
  10. package/dist/types/types/components/WRadios.d.ts +30 -0
  11. package/dist/types/types/components/WSelect.d.ts +34 -0
  12. package/dist/types/types/components/WSwitch.d.ts +34 -0
  13. package/dist/types/types/components/WTable.d.ts +7 -0
  14. package/dist/types/types/components/WTooltip.d.ts +20 -7
  15. package/dist/types/types/components/WTransitions.d.ts +104 -0
  16. package/dist/types/types/components/WTransitions.js +2 -0
  17. package/dist/types/types/components/WTree.d.ts +7 -0
  18. package/dist/types/types/components/index.d.ts +2 -1
  19. package/dist/wave-ui.cjs.js +3 -3
  20. package/dist/wave-ui.css +1 -1
  21. package/dist/wave-ui.esm.js +1711 -1338
  22. package/dist/wave-ui.umd.js +3 -3
  23. package/package.json +1 -1
  24. package/src/wave-ui/components/index.js +0 -1
  25. package/src/wave-ui/components/transitions/w-transition-bounce.vue +2 -1
  26. package/src/wave-ui/components/transitions/w-transition-expand.vue +3 -2
  27. package/src/wave-ui/components/transitions/w-transition-fade.vue +2 -1
  28. package/src/wave-ui/components/transitions/w-transition-scale-fade.vue +2 -1
  29. package/src/wave-ui/components/transitions/w-transition-scale.vue +2 -1
  30. package/src/wave-ui/components/transitions/w-transition-slide-fade.vue +2 -1
  31. package/src/wave-ui/components/transitions/w-transition-slide.vue +2 -1
  32. package/src/wave-ui/components/transitions/w-transition-twist.vue +2 -1
  33. package/src/wave-ui/components/w-accordion/index.vue +10 -5
  34. package/src/wave-ui/components/w-accordion/item.vue +29 -14
  35. package/src/wave-ui/components/w-alert.vue +27 -29
  36. package/src/wave-ui/components/w-autocomplete.vue +626 -192
  37. package/src/wave-ui/components/w-badge.vue +54 -53
  38. package/src/wave-ui/components/w-breadcrumbs.vue +7 -9
  39. package/src/wave-ui/components/w-button/button.vue +21 -23
  40. package/src/wave-ui/components/w-button/index.vue +4 -4
  41. package/src/wave-ui/components/w-card.vue +8 -7
  42. package/src/wave-ui/components/w-checkbox.vue +31 -11
  43. package/src/wave-ui/components/w-checkboxes.vue +21 -3
  44. package/src/wave-ui/components/w-confirm.vue +22 -22
  45. package/src/wave-ui/components/w-dialog.vue +1 -1
  46. package/src/wave-ui/components/w-divider.vue +5 -5
  47. package/src/wave-ui/components/w-drawer.vue +3 -3
  48. package/src/wave-ui/components/w-form-element.vue +2 -2
  49. package/src/wave-ui/components/w-icon.vue +12 -14
  50. package/src/wave-ui/components/w-image.vue +1 -1
  51. package/src/wave-ui/components/w-input.vue +43 -25
  52. package/src/wave-ui/components/w-list.vue +11 -12
  53. package/src/wave-ui/components/w-menu.vue +57 -55
  54. package/src/wave-ui/components/w-notification.vue +4 -4
  55. package/src/wave-ui/components/w-progress.vue +6 -7
  56. package/src/wave-ui/components/w-radio.vue +32 -7
  57. package/src/wave-ui/components/w-radios.vue +28 -3
  58. package/src/wave-ui/components/w-rating.vue +7 -9
  59. package/src/wave-ui/components/w-scrollable.vue +4 -4
  60. package/src/wave-ui/components/w-select.vue +119 -101
  61. package/src/wave-ui/components/w-slider.vue +26 -26
  62. package/src/wave-ui/components/w-spinner.vue +5 -7
  63. package/src/wave-ui/components/w-switch.vue +71 -47
  64. package/src/wave-ui/components/w-table.vue +69 -36
  65. package/src/wave-ui/components/w-tabs/index.vue +21 -24
  66. package/src/wave-ui/components/w-tag.vue +35 -38
  67. package/src/wave-ui/components/w-textarea.vue +22 -22
  68. package/src/wave-ui/components/w-timeline.vue +6 -6
  69. package/src/wave-ui/components/w-toolbar.vue +8 -8
  70. package/src/wave-ui/components/w-tooltip.vue +30 -25
  71. package/src/wave-ui/components/w-tree.vue +35 -16
  72. package/src/wave-ui/core.js +9 -1
  73. package/src/wave-ui/mixins/detachable.js +98 -43
  74. package/src/wave-ui/mixins/ripple.js +2 -2
  75. package/src/wave-ui/scss/_base.scss +82 -17
  76. package/src/wave-ui/scss/_colors.scss +6 -75
  77. package/src/wave-ui/scss/_layout.scss +39 -47
  78. package/src/wave-ui/scss/_ripple.scss +2 -2
  79. package/src/wave-ui/scss/_transitions.scss +19 -19
  80. package/src/wave-ui/scss/_typography.scss +8 -9
  81. package/src/wave-ui/scss/variables/_mixins.scss +24 -25
  82. package/src/wave-ui/scss/variables/_variables.scss +4 -149
  83. package/src/wave-ui/utils/colors.js +7 -4
  84. package/src/wave-ui/utils/config.js +3 -4
  85. package/src/wave-ui/utils/dynamic-css.js +42 -20
  86. package/src/wave-ui/utils/ripple.js +3 -2
  87. package/dist/types/types/components/WApp.d.ts +0 -83
  88. package/src/wave-ui/components/w-app.vue +0 -24
  89. /package/dist/types/types/components/{WApp.js → WAutocomplete.js} +0 -0
@@ -2,6 +2,10 @@
2
2
  * A detachable element is an element that can be appended to another DOM node
3
3
  * (but keeping data-driven Vue DOM refreshes).
4
4
  * This mixin is used by w-tooltip & w-menu.
5
+ *
6
+ * Vue Teleport handles moving the floating content to the right DOM node.
7
+ * Event listeners are auto-attached to the activator slot's root element so
8
+ * callers no longer need the `template(#activator="{ on }") v-on="on"` pattern.
5
9
  */
6
10
 
7
11
  import { consoleWarn } from '../utils/console'
@@ -55,7 +59,11 @@ export default {
55
59
  // Set to true by computeDetachableCoords after positioning, false until then.
56
60
  // Components use this to bind visibility:hidden, so the element is never visible at the
57
61
  // wrong position before its coordinates are calculated.
58
- detachableReady: false
62
+ detachableReady: false,
63
+ // The Vue Teleport target. Stored as data (not computed) so it is resolved lazily at
64
+ // open()-time — after the DOM is committed — rather than during VNode creation where
65
+ // document.querySelector() may return null for elements that are part of the same render batch.
66
+ teleportTarget: null
59
67
  }),
60
68
 
61
69
  computed: {
@@ -74,7 +82,8 @@ export default {
74
82
 
75
83
  let target = this.appendTo || defaultTarget
76
84
  if (target === true) target = defaultTarget
77
- else if (this.appendTo === 'activator') target = this.$el.previousElementSibling || this.$el.nextElementSibling
85
+ // When appendTo is 'activator', teleport into the activator element itself.
86
+ else if (this.appendTo === 'activator') target = this.activatorEl
78
87
  else if (target && !['object', 'string'].includes(typeof target)) target = defaultTarget
79
88
  else if (typeof target === 'object' && !target.nodeType) {
80
89
  target = defaultTarget
@@ -111,7 +120,9 @@ export default {
111
120
  if (typeof document === 'undefined') return null
112
121
  return document.querySelector(this.activator)
113
122
  }
114
- return this.$el.nextElementSibling
123
+ // For slot-based activators the component root ($el) is a comment fragment anchor in
124
+ // Vue 3 multi-root components; nextElementSibling is the activator slot's first real element.
125
+ return this.$el?.nextElementSibling || null
115
126
  },
116
127
  set () {}
117
128
  },
@@ -154,6 +165,7 @@ export default {
154
165
  // next open starts hidden. Done here rather than on close() to let the leave animation play.
155
166
  onAfterLeave () {
156
167
  this.detachableReady = false
168
+ this.detachableEl = null
157
169
  },
158
170
 
159
171
  unbindActivatorDocEvents () {
@@ -166,6 +178,61 @@ export default {
166
178
  }
167
179
  },
168
180
 
181
+ /**
182
+ * Single delegating handler for auto-attached slot-activator DOM events.
183
+ * Reads the current activatorEventHandlers computed each invocation so that changes to
184
+ * `disable`, `showOnHover`, etc. are always reflected without re-attaching.
185
+ */
186
+ _handleActivatorEvent (e) {
187
+ const handler = this.activatorEventHandlers[e.type]
188
+ if (handler) handler(e)
189
+ },
190
+
191
+ /**
192
+ * Attach DOM event listeners directly to the activator slot's root element.
193
+ * Called once from mounted(); a single delegating handler covers all event types so we never
194
+ * need to re-attach when props like `disable` or `showOnHover` change.
195
+ * ! \ This function uses the DOM - NO SSR.
196
+ */
197
+ _attachActivatorListeners () {
198
+ if (typeof document === 'undefined') return
199
+ const el = this.activatorEl
200
+ if (!el) return
201
+
202
+ // Inspect the activator slot's first VNode for pre-declared event handlers.
203
+ // When the slot root already declares onClick / onMouseenter etc. — whether on a native
204
+ // element (w-select, w-autocomplete) or on a component (w-button @click="...") — the
205
+ // parent is managing that event itself. Skip auto-attaching the competing handler to avoid
206
+ // open/close races (both toggle() and the explicit handler firing on the same click).
207
+ // With the new API, the default slot is the activator (no #activator slot used).
208
+ let existingHandlers = {}
209
+ const activatorSlot = this.$slots.activator || this.$slots.default
210
+ if (activatorSlot) {
211
+ const vnodes = activatorSlot()
212
+ const firstVnode = vnodes?.[0]
213
+ existingHandlers = firstVnode?.props || {}
214
+ }
215
+
216
+ this._activatorDomEl = el
217
+ this._activatorAttachedEvents = []
218
+ ;['click', 'mouseenter', 'mouseleave', 'focus', 'blur'].forEach(evt => {
219
+ // Skip if the slot element already binds this event (camelCase Vue prop name: onClick etc.).
220
+ const vueProp = `on${evt.charAt(0).toUpperCase()}${evt.slice(1)}`
221
+ if (existingHandlers[vueProp]) return
222
+ el.addEventListener(evt, this._handleActivatorEvent)
223
+ this._activatorAttachedEvents.push(evt)
224
+ })
225
+ },
226
+
227
+ _detachActivatorListeners () {
228
+ if (!this._activatorDomEl) return
229
+ ;(this._activatorAttachedEvents || []).forEach(evt => {
230
+ this._activatorDomEl.removeEventListener(evt, this._handleActivatorEvent)
231
+ })
232
+ this._activatorDomEl = null
233
+ this._activatorAttachedEvents = []
234
+ },
235
+
169
236
  // ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
170
237
  async open (e) {
171
238
  if (this.disable) return
@@ -181,13 +248,22 @@ export default {
181
248
 
182
249
  // Hide before entering the DOM; handles rapid re-opens where detachableReady is still true.
183
250
  this.detachableReady = false
251
+
252
+ // Resolve the teleport target here, at open()-time, so the DOM is fully committed and
253
+ // detachableDefaultRoot() (for nested menus/tooltips) returns the correct element.
254
+ // Setting teleportTarget and detachableVisible in the same synchronous block lets Vue
255
+ // batch both into a single render — the content is never shown at the wrong location.
256
+ if (typeof document !== 'undefined') this.teleportTarget = this.appendToTarget
257
+
184
258
  this.detachableVisible = true
185
259
 
186
260
  // If the activator is external, there might be multiple,
187
261
  // so on open, the activator will be set to the event target.
188
262
  if (this.activator) this.activatorEl = e.target
189
263
 
190
- await this.insertInDOM()
264
+ // Wait for Vue Teleport to render the floating element into the target DOM node.
265
+ await this.$nextTick()
266
+ this.detachableEl = this.$refs.detachable?.$el || this.$refs.detachable
191
267
 
192
268
  if (this.minWidth === 'activator' && this.activatorEl) {
193
269
  this.activatorWidth = this.activatorEl.offsetWidth
@@ -361,32 +437,6 @@ export default {
361
437
  }
362
438
  },
363
439
 
364
- insertInDOM () {
365
- return new Promise(resolve => {
366
- this.$nextTick(() => {
367
- this.detachableEl = this.$refs.detachable?.$el || this.$refs.detachable
368
-
369
- // Move the tooltip/menu elsewhere in the DOM.
370
- if (this.detachableEl && this.appendToTarget) this.appendToTarget.appendChild(this.detachableEl)
371
- resolve()
372
- })
373
- })
374
- },
375
-
376
- removeFromDOM () {
377
- if (typeof document !== 'undefined') {
378
- document.removeEventListener('mousedown', this.onOutsideMousedown)
379
- }
380
- if (typeof window !== 'undefined') {
381
- window.removeEventListener('resize', this.onResize)
382
- }
383
- if (this.detachableEl?.parentNode) {
384
- this.detachableVisible = false
385
- this.detachableEl.remove()
386
- this.detachableEl = null
387
- }
388
- },
389
-
390
440
  // If the activator is external, add event listeners to the document and check the target is
391
441
  // the activator when toggling.
392
442
  // This way, the activator can be a future DOM element, that is not yet in the DOM.
@@ -401,8 +451,8 @@ export default {
401
451
  eventName = eventName.replace('mouseenter', 'mouseover').replace('mouseleave', 'mouseout')
402
452
  const handlerWrap = e => {
403
453
  // The activator can be a DOM string selector a ref or a DOM node.
404
- if (activatorIsString && e.target?.matches && e.target.matches(this.activator)) handler(e)
405
- else if (e.target === this.activatorEl || this.activatorEl.contains(e.target)) handler(e)
454
+ if (activatorIsString && e.target?.matches?.(this.activator)) handler(e)
455
+ else if (e.target === this.activatorEl || this.activatorEl?.contains(e.target)) handler(e)
406
456
  }
407
457
  document.addEventListener(eventName, handlerWrap)
408
458
  // The event listeners handlers have to be removed the exact same way they have been attached.
@@ -414,14 +464,18 @@ export default {
414
464
  },
415
465
 
416
466
  mounted () {
417
- // If the activator is external.
418
- if (this.activator) this.bindActivatorEvents()
419
-
420
- // If the activator seems to be undefined, it is probably a DOM node or Vue ref,
421
- // so check it on nextTick.
467
+ if (this.activator) {
468
+ // External activator: attach via document-level delegation.
469
+ this.bindActivatorEvents()
470
+ }
422
471
  else {
472
+ // Slot-based activator: auto-attach DOM listeners to the slot's root element on next tick
473
+ // so the slot content is guaranteed to be in the DOM.
423
474
  this.$nextTick(() => {
475
+ // Re-check activator prop (might have resolved from a Vue ref after the tick).
424
476
  if (this.activator) this.bindActivatorEvents()
477
+ else this._attachActivatorListeners()
478
+
425
479
  if (this.modelValue && !this.disable) this.open({ target: this.activatorEl })
426
480
  })
427
481
  }
@@ -438,10 +492,8 @@ export default {
438
492
  unmounted () {
439
493
  this.close()
440
494
 
441
- this.removeFromDOM()
442
-
443
- // Remove the event listeners the exact same way they have been defined.
444
- // Fixes issues on hot-reloading.
495
+ // Clean up slot-activator DOM listeners and external-activator document listeners.
496
+ this._detachActivatorListeners()
445
497
  this.unbindActivatorDocEvents()
446
498
  },
447
499
 
@@ -451,6 +503,8 @@ export default {
451
503
  this.unbindActivatorDocEvents()
452
504
  if (!disabled) this.bindActivatorEvents()
453
505
  }
506
+ // For slot-based activators, _handleActivatorEvent always reads the current
507
+ // activatorEventHandlers computed which already respects `disable`, so no re-attach needed.
454
508
  if (disabled) this.close()
455
509
  else if (this.modelValue) this.open({ target: this.activatorEl })
456
510
  },
@@ -461,9 +515,10 @@ export default {
461
515
  else if (!bool) this.close()
462
516
  }
463
517
  },
518
+
519
+ // Keep teleportTarget in sync when the appendTo prop changes at runtime.
464
520
  appendTo () {
465
- this.removeFromDOM()
466
- this.insertInDOM()
521
+ if (typeof document !== 'undefined') this.teleportTarget = this.appendToTarget
467
522
  }
468
523
  }
469
524
  }
@@ -21,7 +21,7 @@ export default {
21
21
  * Resolve host for applyRipple. Vue (and some browsers) can leave `event.currentTarget` null
22
22
  * on delegated handlers, which would otherwise skip the ripple entirely.
23
23
  */
24
- onRipple (e, hostEl) {
24
+ onRipple (e, hostEl, options) {
25
25
  if (!this.rippleActive || !e) return
26
26
  let host = hostEl ?? e.currentTarget
27
27
  if (!host?.getBoundingClientRect) {
@@ -33,7 +33,7 @@ export default {
33
33
  host = this.$el
34
34
  }
35
35
  if (!host?.getBoundingClientRect) return
36
- applyRipple(host, e)
36
+ applyRipple(host, e, options)
37
37
  }
38
38
  }
39
39
  }
@@ -1,11 +1,43 @@
1
- @use "sass:map";
2
1
  @use "variables" as *;
3
2
 
4
3
  // The CSS variables are used in the dynamic-css.js file in order to reuse the same SCSS
5
4
  // variable presets.
6
5
  :root {
7
- --w-base-increment: #{$base-increment};
6
+ --w-base-font-size: 14px;
7
+ // Keeps spacing in sync when only --w-base-font-size is overridden (matches former Sass rounding).
8
+ --w-base-increment: round(nearest, calc(var(--w-base-font-size) / 4), 1px);
9
+ // Sass-only: must match $css-scope in _layout.scss and what dynamic-css reads for injected rules.
8
10
  --w-css-scope: #{$css-scope};
11
+ --w-layout-padding: 16px;
12
+ --w-border-radius: 4px;
13
+ --w-border-width: 1px;
14
+ --w-border-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 12%, transparent)};
15
+ --w-border: var(--w-border-width) solid var(--w-border-color);
16
+ --w-transition-duration: 0.25s;
17
+ --w-transition-duration-fast: 0.15s;
18
+ --w-transition-timing-fast-out-slow-in: cubic-bezier(0.4, 0, 0.2, 1);
19
+ --w-box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2),
20
+ 0 2px 2px 0 rgba(0, 0, 0, 0.15),
21
+ 0 1px 5px 0 rgba(0, 0, 0, 0.15);
22
+ --w-form-field-height: round(nearest, calc(2 * var(--w-base-font-size)), 1px);
23
+ // Even px step (same as former 2 * round(1.3 * $base-font-size / 2)).
24
+ --w-small-form-el-size: round(nearest, calc(1.3 * var(--w-base-font-size)), 2px);
25
+ --w-scrollbar-size: 8px;
26
+
27
+ @for $i from -6 through -1 {
28
+ --w-shadow-n#{-1 * $i}: 0 0 round(nearest, calc(#{$i} * var(--w-base-increment)), 1px) rgba(0, 0, 0, max(0.15, calc(0.15 * #{-$i} / 2))) inset;
29
+ }
30
+ --w-shadow-0: none;
31
+ @for $i from 1 through 6 {
32
+ --w-shadow-#{$i}: 0 0 1px rgba(0, 0, 0, 0.1),
33
+ round(nearest, calc(var(--w-base-increment) * #{$i} / 4), 1px) round(nearest, calc(var(--w-base-increment) * #{$i} / 4), 1px) round(nearest, calc(#{$i} * var(--w-base-increment)), 1px) rgba(0, 0, 0, max(0.15, calc(0.15 * #{$i} / 2)));
34
+ }
35
+
36
+ --w-base-color-muted: #{color-mix(in srgb, var(--w-base-color) 70%, transparent)};
37
+ --w-overlay-scrim-color: #{color-mix(in srgb, #000 30%, transparent)};
38
+ --w-surface-hover-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 5%, transparent)};
39
+ --w-surface-active-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 8%, transparent)};
40
+ --w-surface-selected-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 15%, transparent)};
9
41
  --w-contrast-bg-o025-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 2.5%, transparent)};
10
42
  --w-contrast-bg-o05-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 5%, transparent)};
11
43
  --w-contrast-bg-o1-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 10%, transparent)};
@@ -18,26 +50,59 @@
18
50
  --w-contrast-bg-o8-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 80%, transparent)};
19
51
  --w-contrast-bg-o9-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 90%, transparent)};
20
52
 
53
+ --w-detachable-bg-color: var(--w-base-bg-color);
54
+ --w-detachable-color: var(--w-base-color);
55
+ --w-confirm-bg-color: var(--w-detachable-bg-color);
56
+ --w-confirm-color: var(--w-detachable-color);
57
+ --w-dialog-bg-color: var(--w-base-bg-color);
58
+ --w-divider-color: var(--w-border-color);
59
+ --w-drawer-max-size: 380px;
60
+ --w-drawer-bg-color: var(--w-base-bg-color);
61
+ --w-menu-bg-color: var(--w-detachable-bg-color);
62
+ --w-menu-color: var(--w-detachable-color);
63
+ --w-progress-bg-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 15%, transparent)};
64
+ --w-rating-bg-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 25%, transparent)};
65
+ --w-slider-height: var(--w-base-increment);
66
+ --w-slider-track-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 15%, transparent)};
67
+ --w-slider-thumb-button-bg-color: var(--w-base-bg-color);
68
+ --w-slider-thumb-label-bg-color: var(--w-base-bg-color);
69
+ --w-slider-thumb-label-color: #{color-mix(in srgb, var(--w-base-color) 75%, transparent)};
70
+ --w-slider-step-label-bg-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 20%, transparent)};
71
+ --w-slider-step-label-color: #{color-mix(in srgb, var(--w-base-color) 50%, transparent)};
72
+ --w-switch-inactive-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 25%, transparent)};
73
+ --w-switch-thumb-color: var(--w-base-bg-color);
74
+ --w-table-tr-odd-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 2%, transparent)};
75
+ --w-table-tr-hover-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 5%, transparent)};
76
+ --w-table-color: #{color-mix(in srgb, var(--w-contrast-color) 70%, transparent)};
77
+ --w-textarea-line-height: 1.2;
78
+ --w-timeline-bullet-color: var(--w-base-bg-color);
79
+ --w-timeline-bg-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 25%, transparent)};
80
+ --w-toolbar-max-size: 380px;
81
+ --w-toolbar-bg-color: var(--w-base-bg-color);
82
+ --w-tooltip-bg-color: var(--w-detachable-bg-color);
83
+ --w-tooltip-color: var(--w-detachable-color);
84
+ --w-tooltip-border-color: var(--w-border-color);
85
+
21
86
  background-color: var(--w-base-bg-color);
22
87
  color: var(--w-base-color);
23
88
  }
24
89
 
25
90
  :root[data-theme="light"] {
26
- --w-base-bg-color: #{map.get($theme-light, 'base-bg-color')};
27
- --w-base-color: #{map.get($theme-light, 'base-color')};
28
- --w-contrast-bg-color: #{map.get($theme-light, 'contrast-bg-color')};
29
- --w-contrast-color: #{map.get($theme-light, 'contrast-color')};
30
- --w-caption-color: #{map.get($theme-light, 'caption-color')};
31
- --w-disabled-color: #{map.get($theme-light, 'disabled-color')};
91
+ --w-base-bg-color: #fff;
92
+ --w-base-color: #000;
93
+ --w-contrast-bg-color: #000;
94
+ --w-contrast-color: #fff;
95
+ --w-caption-color: #a0a0a0;
96
+ --w-disabled-color: #ccc;
32
97
  }
33
98
 
34
99
  :root[data-theme="dark"] {
35
- --w-base-bg-color: #{map.get($theme-dark, 'base-bg-color')};
36
- --w-base-color: #{map.get($theme-dark, 'base-color')};
37
- --w-contrast-bg-color: #{map.get($theme-dark, 'contrast-bg-color')};
38
- --w-contrast-color: #{map.get($theme-dark, 'contrast-color')};
39
- --w-caption-color: #{map.get($theme-dark, 'caption-color')};
40
- --w-disabled-color: #{map.get($theme-dark, 'disabled-color')};
100
+ --w-base-bg-color: #222;
101
+ --w-base-color: #fff;
102
+ --w-contrast-bg-color: #fff;
103
+ --w-contrast-color: #000;
104
+ --w-caption-color: #6e6e6e;
105
+ --w-disabled-color: #4a4a4a;
41
106
  }
42
107
 
43
108
  * {
@@ -72,13 +137,13 @@ a {text-decoration: none;}
72
137
  }
73
138
 
74
139
  .w-main {
75
- padding-left: 3 * $base-increment;
76
- padding-right: 3 * $base-increment;
140
+ padding-left: calc(3 * var(--w-base-increment));
141
+ padding-right: calc(3 * var(--w-base-increment));
77
142
  }
78
143
 
79
144
  // Structure classes.
80
145
  // ----------------------------------------------
81
146
  .content-wrap {
82
147
  position: relative;
83
- padding: $layout-padding;
148
+ padding: var(--w-layout-padding);
84
149
  }
@@ -1,4 +1,3 @@
1
- @use 'sass:color';
2
1
  @use 'variables' as *;
3
2
 
4
3
  #{$css-scope} {
@@ -29,82 +28,14 @@
29
28
  // For each color, create a [color] and a [color]--bg associated classes,
30
29
  // + 6 shades lighter and 6 shades darker.
31
30
  @each $label, $color in $colors {
32
- .#{$label}--bg {background-color: $color;}
33
- .#{$label} {color: $color;}
31
+ .#{$label}--bg {background-color: var(--w-#{$label}-color);}
32
+ .#{$label} {color: var(--w-#{$label}-color);}
34
33
 
35
34
  @for $i from 1 through 6 {
36
- $light-increment: 7.5;
37
- $light-offset: 0;
38
- $dark-increment: 6.2;
39
- // Some color shades need bigger or smaller increments to end up with the same scale.
40
- @if $label == 'deep-orange' {
41
- $light-increment: 6.4;
42
- }
43
- @if $label == 'orange' {
44
- }
45
- @else if $label == 'green' {
46
- $light-increment: 7.6;
47
- $dark-increment: 5.7;
48
- }
49
- @else if $label == 'amber' {
50
- }
51
- @else if $label == 'pink' {
52
- $light-increment: 6.7;
53
- $light-offset: -4;
54
- }
55
- @else if $label == 'red' {
56
- $light-increment: 6.5;
57
- $light-offset: -1;
58
- }
59
- @else if $label == 'indigo' {
60
- $light-increment: 8;
61
- $dark-increment: 5.7;
62
- }
63
- @else if $label == 'deep-purple' {
64
- $light-increment: 8;
65
- $dark-increment: 5.7;
66
- }
67
- @else if $label == 'light-blue' {
68
- $light-increment: 7.8;
69
- }
70
- @else if $label == 'light-green' {
71
- $light-increment: 6;
72
- $light-offset: -5;
73
- }
74
- @else if $label == 'lime' {
75
- $light-increment: 6.2;
76
- $light-offset: -6;
77
- }
78
- @else if $label == 'yellow' {
79
- $light-increment: 5.5;
80
- $light-offset: -8;
81
- }
82
- @else if $label == 'purple' {
83
- $light-increment: 6.5;
84
- $light-offset: -8.5;
85
- }
86
- @else if $label == 'cyan' {
87
- $light-increment: 9.4;
88
- $light-offset: 6.5;
89
- $dark-increment: 5.7;
90
- }
91
- @else if $label == 'teal' {
92
- $light-increment: 9.6;
93
- $light-offset: 5;
94
- $dark-increment: 5.4;
95
- }
96
- @else if $label == 'blue' {
97
- $light-increment: 6.8;
98
- $dark-increment: 6.8;
99
- }
100
- @else if $label == 'brown' {
101
- $light-increment: 8.8;
102
- $dark-increment: 5;
103
- }
104
- .#{$label}-light#{$i}--bg {background-color: color.adjust($color, $lightness: $light-increment * $i * 1% - $light-offset);}
105
- .#{$label}-light#{$i} {color: color.adjust($color, $lightness: $light-increment * $i * 1% - $light-offset);}
106
- .#{$label}-dark#{$i}--bg {background-color: color.adjust($color, $lightness: - $dark-increment * $i * 1%);}
107
- .#{$label}-dark#{$i} {color: color.adjust($color, $lightness: - $dark-increment * $i * 1%);}
35
+ .#{$label}-light#{$i}--bg {background-color: var(--w-#{$label}-light#{$i}-color);}
36
+ .#{$label}-light#{$i} {color: var(--w-#{$label}-light#{$i}-color);}
37
+ .#{$label}-dark#{$i}--bg {background-color: var(--w-#{$label}-dark#{$i}-color);}
38
+ .#{$label}-dark#{$i} {color: var(--w-#{$label}-dark#{$i}-color);}
108
39
  }
109
40
  }
110
41
 
@@ -1,4 +1,3 @@
1
- @use "sass:math";
2
1
  @use 'variables' as *;
3
2
 
4
3
  // All these CSS classes will not be generated if the $use-layout-classes is set to false.
@@ -73,18 +72,18 @@
73
72
  // Borders (from 0 to 6), radii (from 0 to 6 + `m` for max).
74
73
  // ----------------------------------------------
75
74
  @for $i from 1 through 6 {
76
- .bd#{$i} {border: $i + px solid $border-color;}
75
+ .bd#{$i} {border: #{$i}px solid var(--w-border-color);}
77
76
  }
78
- .bdx1 {border-left: 1px solid $border-color;border-right: 1px solid $border-color;}
79
- .bdy1 {border-top: 1px solid $border-color;border-bottom: 1px solid $border-color;}
80
- .bdl1 {border-left: 1px solid $border-color;}
81
- .bdr1 {border-right: 1px solid $border-color;}
82
- .bdt1 {border-top: 1px solid $border-color;}
83
- .bdb1 {border-bottom: 1px solid $border-color;}
77
+ .bdx1 {border-left: var(--w-border);border-right: var(--w-border);}
78
+ .bdy1 {border-top: var(--w-border);border-bottom: var(--w-border);}
79
+ .bdl1 {border-left: var(--w-border);}
80
+ .bdr1 {border-right: var(--w-border);}
81
+ .bdt1 {border-top: var(--w-border);}
82
+ .bdb1 {border-bottom: var(--w-border);}
84
83
  .bd0 {border: none;}
85
84
 
86
85
  @for $i from 1 through 6 {
87
- .bdrs#{$i} {border-radius: math.round($i * $base-increment);}
86
+ .bdrs#{$i} {border-radius: calc(#{$i} * var(--w-base-increment));}
88
87
  }
89
88
  .bdrsr {border-radius: 999em;}
90
89
  .bdrsm {border-radius: 100%;}
@@ -95,36 +94,33 @@
95
94
  // ----------------------------------------------
96
95
  @for $i from -6 through -1 {
97
96
  .sh#{$i} {
98
- box-shadow: 0 0 (-1 * math.round($i * $base-increment)) rgba(#000, max(0.15 * divide(-$i, 2), 0.15)) inset;
97
+ box-shadow: var(--w-shadow-n#{-1 * $i});
99
98
  }
100
99
  }
101
100
  @for $i from 1 through 6 {
102
101
  .sh#{$i} {
103
- box-shadow: 0 0 1px rgba(#000, 0.1),
104
- math.round($base-increment * divide($i, 4)) math.round($base-increment * divide($i, 4)) math.round($i * $base-increment) rgba(#000, max(0.15 * divide($i, 2), 0.15));
102
+ box-shadow: var(--w-shadow-#{$i});
105
103
  }
106
104
  }
107
- .sh0 {box-shadow: none;}
105
+ .sh0 {box-shadow: var(--w-shadow-0);}
108
106
  // ----------------------------------------------
109
107
 
110
108
  // Spaces.
111
109
  // ----------------------------------------------
112
110
  // Margin.
113
111
  @for $i from 0 through 12 {
114
- $number: math.round($i * $base-increment);
115
- .ma#{$i} {margin: $number;}
116
- @if ($i > 0) {.ma-#{$i} {margin: -$number;}}
112
+ .ma#{$i} {margin: calc(#{$i} * var(--w-base-increment));}
113
+ @if ($i > 0) {.ma-#{$i} {margin: calc(-#{$i} * var(--w-base-increment));}}
117
114
  }
118
115
  .maa {margin: auto;}
119
116
  .ma0 {margin: 0;}
120
117
 
121
118
  @for $i from 0 through 12 {
122
- $number: math.round($i * $base-increment);
123
- .mx#{$i} {margin-left: $number;margin-right: $number;}
124
- .my#{$i} {margin-top: $number;margin-bottom: $number;}
119
+ .mx#{$i} {margin-left: calc(#{$i} * var(--w-base-increment));margin-right: calc(#{$i} * var(--w-base-increment));}
120
+ .my#{$i} {margin-top: calc(#{$i} * var(--w-base-increment));margin-bottom: calc(#{$i} * var(--w-base-increment));}
125
121
  @if ($i > 0) {
126
- .mx-#{$i} {margin-left: -$number;margin-right: -$number;}
127
- .my-#{$i} {margin-top: -$number;margin-bottom: -$number;}
122
+ .mx-#{$i} {margin-left: calc(-#{$i} * var(--w-base-increment));margin-right: calc(-#{$i} * var(--w-base-increment));}
123
+ .my-#{$i} {margin-top: calc(-#{$i} * var(--w-base-increment));margin-bottom: calc(-#{$i} * var(--w-base-increment));}
128
124
  }
129
125
  }
130
126
  .mxa {margin-left: auto;margin-right: auto;}
@@ -133,16 +129,15 @@
133
129
  .my0 {margin-top: 0;margin-bottom: 0;}
134
130
 
135
131
  @for $i from 0 through 12 {
136
- $number: math.round($i * $base-increment);
137
- .mt#{$i} {margin-top: $number;}
138
- .mr#{$i} {margin-right: $number;}
139
- .mb#{$i} {margin-bottom: $number;}
140
- .ml#{$i} {margin-left: $number;}
132
+ .mt#{$i} {margin-top: calc(#{$i} * var(--w-base-increment));}
133
+ .mr#{$i} {margin-right: calc(#{$i} * var(--w-base-increment));}
134
+ .mb#{$i} {margin-bottom: calc(#{$i} * var(--w-base-increment));}
135
+ .ml#{$i} {margin-left: calc(#{$i} * var(--w-base-increment));}
141
136
  @if ($i > 0) {
142
- .mt-#{$i} {margin-top: -$number;}
143
- .mr-#{$i} {margin-right: -$number;}
144
- .mb-#{$i} {margin-bottom: -$number;}
145
- .ml-#{$i} {margin-left: -$number;}
137
+ .mt-#{$i} {margin-top: calc(-#{$i} * var(--w-base-increment));}
138
+ .mr-#{$i} {margin-right: calc(-#{$i} * var(--w-base-increment));}
139
+ .mb-#{$i} {margin-bottom: calc(-#{$i} * var(--w-base-increment));}
140
+ .ml-#{$i} {margin-left: calc(-#{$i} * var(--w-base-increment));}
146
141
  }
147
142
  }
148
143
  .mta {margin-top: auto;}
@@ -156,25 +151,22 @@
156
151
 
157
152
  // Padding.
158
153
  @for $i from 1 through 12 {
159
- $number: math.round($i * $base-increment);
160
- .pa#{$i} {padding: $number;}
154
+ .pa#{$i} {padding: calc(#{$i} * var(--w-base-increment));}
161
155
  }
162
156
  .pa0 {padding: 0;}
163
157
 
164
158
  @for $i from 0 through 12 {
165
- $number: math.round($i * $base-increment);
166
- .px#{$i} {padding-left: $number;padding-right: $number;}
167
- .py#{$i} {padding-top: $number;padding-bottom: $number;}
159
+ .px#{$i} {padding-left: calc(#{$i} * var(--w-base-increment));padding-right: calc(#{$i} * var(--w-base-increment));}
160
+ .py#{$i} {padding-top: calc(#{$i} * var(--w-base-increment));padding-bottom: calc(#{$i} * var(--w-base-increment));}
168
161
  }
169
162
  .px0 {padding-left: 0;padding-right: 0;}
170
163
  .py0 {padding-top: 0;padding-bottom: 0;}
171
164
 
172
165
  @for $i from 0 through 12 {
173
- $number: math.round($i * $base-increment);
174
- .pt#{$i} {padding-top: $number;}
175
- .pr#{$i} {padding-right: $number;}
176
- .pb#{$i} {padding-bottom: $number;}
177
- .pl#{$i} {padding-left: $number;}
166
+ .pt#{$i} {padding-top: calc(#{$i} * var(--w-base-increment));}
167
+ .pr#{$i} {padding-right: calc(#{$i} * var(--w-base-increment));}
168
+ .pb#{$i} {padding-bottom: calc(#{$i} * var(--w-base-increment));}
169
+ .pl#{$i} {padding-left: calc(#{$i} * var(--w-base-increment));}
178
170
  }
179
171
  .pt0 {padding-top: 0;}
180
172
  .pr0 {padding-right: 0;}
@@ -184,14 +176,14 @@
184
176
 
185
177
  // Sizes.
186
178
  // ----------------------------------------------
187
- // In all the sizes below, math.round(x / 2) * 2 to always have even numbers.
179
+ // In all the sizes below, round(nearest, …, 1px) matches former Sass rounding for px base sizes.
188
180
  // Different heights with a mix of odd and even numbers will misalign
189
181
  // when vertically centering (vertical-align or align-items center).
190
- .size--xs {font-size: math.round(0.8 * $base-font-size);}
191
- .size--sm {font-size: math.round(0.9 * $base-font-size);}
192
- .size--md {font-size: math.round(1 * $base-font-size);}
193
- .size--lg {font-size: math.round(1.2 * $base-font-size);}
194
- .size--xl {font-size: math.round(1.4 * $base-font-size);}
182
+ .size--xs {font-size: round(nearest, calc(0.8 * var(--w-base-font-size)), 1px);}
183
+ .size--sm {font-size: round(nearest, calc(0.9 * var(--w-base-font-size)), 1px);}
184
+ .size--md {font-size: round(nearest, calc(1 * var(--w-base-font-size)), 1px);}
185
+ .size--lg {font-size: round(nearest, calc(1.2 * var(--w-base-font-size)), 1px);}
186
+ .size--xl {font-size: round(nearest, calc(1.4 * var(--w-base-font-size)), 1px);}
195
187
  // ----------------------------------------------
196
188
 
197
189
  // Grid system.
@@ -228,6 +220,6 @@
228
220
  }
229
221
 
230
222
  @for $i from 1 through 12 {
231
- .w-flex.gap#{$i}, .w-grid.gap#{$i} {gap: math.round($base-increment * $i);}
223
+ .w-flex.gap#{$i}, .w-grid.gap#{$i} {gap: calc(#{$i} * var(--w-base-increment));}
232
224
  }
233
225
  .w-flex.gap0, .w-grid.gap0 {gap: 0;}
@@ -20,13 +20,13 @@
20
20
  transform: scale(0);
21
21
  opacity: 0.5;
22
22
  // currentColor is often invisible on filled buttons (e.g. white on white); use a neutral ink.
23
- background-color: rgba(#000, 0.22);
23
+ background-color: color-mix(in srgb, #000 22%, transparent);
24
24
  will-change: transform, opacity;
25
25
  animation: w-ripple-ink-expand 0.65s ease-out forwards;
26
26
  }
27
27
 
28
28
  html[data-theme='dark'] .w-ripple__ink {
29
- background-color: rgba(#fff, 0.2);
29
+ background-color: color-mix(in srgb, #fff 20%, transparent);
30
30
  }
31
31
 
32
32
  @keyframes w-ripple-ink-expand {