webcake-ui-kit 1.1.32 → 1.1.34

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webcake-ui-kit",
3
- "version": "1.1.32",
3
+ "version": "1.1.34",
4
4
  "description": "UI Kit for Vue 2 && 3 - Pure Options API",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
@@ -25,10 +25,17 @@
25
25
  </div>
26
26
  <template #footer>
27
27
  <slot name="footer">
28
- <Button :variant="cancelVariant" class="ui-alert-dialog-btn" @click="handleCancel">{{ cancelText }}</Button>
29
- <Button :variant="okVariant" :loading="confirmLoading" class="ui-alert-dialog-btn" @click="handleOk">{{
30
- okText
28
+ <Button size="sm" :variant="cancelVariant" class="ui-alert-dialog-btn" @click="handleCancel">{{
29
+ cancelText
31
30
  }}</Button>
31
+ <Button
32
+ size="sm"
33
+ :variant="okVariant"
34
+ :loading="confirmLoading"
35
+ class="ui-alert-dialog-btn"
36
+ @click="handleOk"
37
+ >{{ okText }}</Button
38
+ >
32
39
  </slot>
33
40
  </template>
34
41
  </Dialog>
@@ -11,6 +11,8 @@
11
11
  font-weight: var(--paragraph-bold-font-weight);
12
12
  white-space: nowrap;
13
13
  transition: box-shadow 0.15s ease;
14
+ border-width: 1px;
15
+ border-style: solid;
14
16
  }
15
17
 
16
18
  .ui-badge--round-default {
@@ -21,35 +23,39 @@
21
23
  }
22
24
 
23
25
  .ui-badge--primary {
24
- background: var(--primary-brand-bg);
25
- color: var(--inverse-fg);
26
+ background: var(--positive-subtle);
27
+ color: var(--primary-brand-fg);
28
+ border-color: var(--positive-border-subtle);
26
29
  }
27
30
  .ui-badge--secondary {
28
31
  background: var(--secondary-bg);
29
32
  color: var(--secondary-fg);
33
+ border: none;
30
34
  }
31
35
  .ui-badge--outline {
32
36
  background: var(--outline);
33
37
  color: var(--primary-fg);
34
- border-width: 1px;
35
- border-style: solid;
36
38
  border-color: var(--border-primary);
37
39
  }
38
40
  .ui-badge--ghost {
39
41
  background: var(--ghost);
40
42
  color: var(--primary-fg);
43
+ border: none;
41
44
  }
42
45
  .ui-badge--destructive {
43
- background: var(--destructive);
44
- color: var(--destructive-inverse-fg);
46
+ background: var(--destructive-subtle);
47
+ color: var(--destructive);
48
+ border-color: var(--destructive-border-subtle);
45
49
  }
46
50
  .ui-badge--info {
47
- background: var(--blue-500);
48
- color: var(--inverse-fg);
51
+ background: var(--infor-subtle);
52
+ color: var(--infor);
53
+ border-color: var(--infor-border-subtle);
49
54
  }
50
55
  .ui-badge--warning {
51
- background: var(--yellow-500);
52
- color: var(--inverse-fg);
56
+ background: var(--warning-subtle);
57
+ color: var(--warning);
58
+ border-color: var(--warning-border-subtle);
53
59
  }
54
60
 
55
61
  .ui-badge:focus-visible {
@@ -48,7 +48,8 @@ export default {
48
48
  variant: {
49
49
  type: String,
50
50
  default: 'primary',
51
- validator: v => ['primary', 'neutral', 'secondary', 'outline', 'ghost', 'destructive', 'link'].includes(v)
51
+ validator: v =>
52
+ ['primary', 'neutral', 'secondary', 'outline', 'ghost', 'destructive', 'link', 'ghost-destructive'].includes(v)
52
53
  },
53
54
  size: {
54
55
  type: String,
@@ -4,12 +4,12 @@
4
4
  .ui-btn {
5
5
  display: inline-flex;
6
6
  align-items: center;
7
- justify-content: center;
8
7
  border: 1px solid transparent;
9
8
  margin: 0;
10
9
  cursor: pointer;
11
10
  font-family: var(--font-family-body);
12
11
  font-weight: var(--paragraph-medium-font-weight);
12
+ justify-content: center;
13
13
  text-align: center;
14
14
  white-space: nowrap;
15
15
  transition:
@@ -218,6 +218,17 @@
218
218
  background: var(--destructive-hover);
219
219
  }
220
220
 
221
+ /* ghost-destructive */
222
+ .ui-btn--ghost-destructive {
223
+ background: var(--ghost);
224
+ color: var(--destructive);
225
+ }
226
+
227
+ .ui-btn--ghost-destructive:hover,
228
+ .ui-btn--ghost-destructive:active {
229
+ background: var(--destructive-subtle);
230
+ }
231
+
221
232
  /* Link — text-only button on transparent ghost bg, brand-colored. */
222
233
  .ui-btn--link {
223
234
  background: var(--ghost);
@@ -252,7 +263,8 @@
252
263
  border-color: var(--border-focus);
253
264
  }
254
265
 
255
- .ui-btn--destructive:focus-visible {
266
+ .ui-btn--destructive:focus-visible,
267
+ .ui-btn--ghost-destructive:focus-visible {
256
268
  box-shadow: 0 0 0 3px var(--focus-ring-error);
257
269
  }
258
270
 
@@ -265,6 +277,13 @@
265
277
  pointer-events: none;
266
278
  }
267
279
 
280
+ .ui-btn:not(.ui-btn--ghost):not(.ui-btn--link):disabled {
281
+ background: var(--muted-bg);
282
+ color: var(--muted-fg);
283
+ border: 1px solid var(--border-primary);
284
+ box-shadow: var(--shadow-xs);
285
+ }
286
+
268
287
  /* ============================================================
269
288
  Icon-only — square button, uniform padding per size.
270
289
  Width = height; default slot content rendered in .ui-btn__icon
@@ -31,3 +31,7 @@
31
31
  position: relative;
32
32
  z-index: 1;
33
33
  }
34
+
35
+ .ui-button-group .ui-btn--outline:disabled {
36
+ background: var(--secondary-hover) !important;
37
+ }
@@ -8,18 +8,19 @@
8
8
  /* Weekday header row — 7 fixed 36px tracks, muted labels. */
9
9
  .ui-cal-view__weekdays {
10
10
  display: grid;
11
- grid-template-columns: repeat(7, 32px);
11
+ grid-template-columns: repeat(7, 36px);
12
12
  }
13
13
 
14
14
  .ui-cal-view__weekday {
15
15
  height: 32px;
16
- width: 32px;
16
+ width: 36px;
17
17
  display: inline-flex;
18
18
  align-items: center;
19
19
  justify-content: center;
20
20
  color: var(--muted-fg);
21
- font-size: var(--paragraph-small-font-size);
22
- line-height: var(--paragraph-small-line-height);
21
+ font-size: var(--paragraph-mini-font-size);
22
+ line-height: var(--paragraph-mini-line-height);
23
+ letter-spacing: var(--paragraph-mini-letter-spacing);
23
24
  }
24
25
 
25
26
  /*
@@ -28,15 +29,15 @@
28
29
  */
29
30
  .ui-cal-view__grid {
30
31
  display: grid;
31
- grid-template-columns: repeat(7, 32px);
32
+ grid-template-columns: repeat(7, 36px);
32
33
  row-gap: 1px;
33
34
  column-gap: 0;
34
35
  }
35
36
 
36
37
  .ui-cal-cell {
37
38
  position: relative;
38
- width: 32px;
39
- height: 32px;
39
+ width: 36px;
40
+ height: 36px;
40
41
  padding: 0;
41
42
  border: 0;
42
43
  background: transparent;
@@ -85,8 +86,8 @@
85
86
  display: inline-flex;
86
87
  align-items: center;
87
88
  justify-content: center;
88
- width: 32px;
89
- height: 32px;
89
+ width: 36px;
90
+ height: 36px;
90
91
  border-radius: var(--rounded-lg);
91
92
  transition:
92
93
  background-color 0.12s ease,
@@ -11,15 +11,15 @@
11
11
  // (Vue 3) and webpack (Vue 2) bundlers.
12
12
  import dayjs from 'dayjs'
13
13
 
14
- import customParseFormat from 'dayjs/plugin/customParseFormat.js'
15
- import localeData from 'dayjs/plugin/localeData.js'
16
- import weekday from 'dayjs/plugin/weekday.js'
17
- import weekOfYear from 'dayjs/plugin/weekOfYear.js'
18
- import isoWeek from 'dayjs/plugin/isoWeek.js'
19
- import advancedFormat from 'dayjs/plugin/advancedFormat.js'
20
- import isSameOrBefore from 'dayjs/plugin/isSameOrBefore.js'
21
- import isSameOrAfter from 'dayjs/plugin/isSameOrAfter.js'
22
- import quarterOfYear from 'dayjs/plugin/quarterOfYear.js'
14
+ import customParseFormat from 'dayjs/plugin/customParseFormat'
15
+ import localeData from 'dayjs/plugin/localeData'
16
+ import weekday from 'dayjs/plugin/weekday'
17
+ import weekOfYear from 'dayjs/plugin/weekOfYear'
18
+ import isoWeek from 'dayjs/plugin/isoWeek'
19
+ import advancedFormat from 'dayjs/plugin/advancedFormat'
20
+ import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'
21
+ import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'
22
+ import quarterOfYear from 'dayjs/plugin/quarterOfYear'
23
23
 
24
24
  dayjs.extend(customParseFormat)
25
25
  dayjs.extend(localeData)
@@ -1,7 +1,7 @@
1
1
  .ui-time-panel {
2
2
  display: flex;
3
3
  gap: var(--spacing-3xs);
4
- height: 200px;
4
+ height: 220px;
5
5
  font-family: var(--font-family-body);
6
6
  }
7
7
 
@@ -81,8 +81,10 @@
81
81
  >
82
82
  <div class="ui-dialog-footer__inner">
83
83
  <slot name="footer">
84
- <Button :variant="cancelVariant" @click="handleCancel">{{ cancelText }}</Button>
85
- <Button :variant="okVariant" :loading="confirmLoading" @click="handleOk">{{ okText }}</Button>
84
+ <Button size="sm" :variant="cancelVariant" @click="handleCancel">{{ cancelText }}</Button>
85
+ <Button size="sm" :variant="okVariant" :loading="confirmLoading" @click="handleOk">{{
86
+ okText
87
+ }}</Button>
86
88
  </slot>
87
89
  </div>
88
90
  </div>
@@ -95,7 +97,7 @@
95
97
 
96
98
  <script>
97
99
  import { acquire, release } from '../../floating/portal-root.js'
98
- import { bumpTo } from '../../floating/layer-manager.js'
100
+ import { bumpTo, nextZIndex } from '../../floating/layer-manager.js'
99
101
  import Button from '../button/Button.vue'
100
102
  import WkiMinus from '../../icons/Minus.vue'
101
103
  import WkiUnfoldVertical from '../../icons/UnfoldVertical.vue'
@@ -143,7 +145,8 @@ export default {
143
145
  hasOpened: false,
144
146
  isVisible: false,
145
147
  minimized: false,
146
- isFullscreen: false
148
+ isFullscreen: false,
149
+ activeZIndex: 0
147
150
  }
148
151
  },
149
152
  computed: {
@@ -151,7 +154,7 @@ export default {
151
154
  return this.modelValue !== undefined ? this.modelValue : this.open
152
155
  },
153
156
  rootStyle() {
154
- return { zIndex: this.zIndex }
157
+ return { zIndex: this.activeZIndex || this.zIndex }
155
158
  },
156
159
  maskStyle() {
157
160
  if (!this.blur) return {}
@@ -225,6 +228,10 @@ export default {
225
228
  beforeUnmount() {
226
229
  this.cleanup()
227
230
  },
231
+ // eslint-disable-next-line vue/no-deprecated-destroyed-lifecycle, vue/no-dupe-keys
232
+ beforeDestroy() {
233
+ this.cleanup()
234
+ },
228
235
  methods: {
229
236
  setOpen(v) {
230
237
  this.$emit('change', v)
@@ -260,6 +267,7 @@ export default {
260
267
  onOpen() {
261
268
  this.hasOpened = true
262
269
  bumpTo(this.zIndex)
270
+ this.activeZIndex = nextZIndex()
263
271
  this.lockBody()
264
272
  if (typeof document !== 'undefined') {
265
273
  this._prevFocus = document.activeElement
@@ -5,6 +5,7 @@
5
5
  :offset="4"
6
6
  :close-on-esc="closeOnEsc"
7
7
  :disabled="disabled"
8
+ :persistent="persistent"
8
9
  @change="onMenuChange"
9
10
  @open="$emit('open')"
10
11
  @close="$emit('close')"
@@ -68,7 +69,7 @@ export default {
68
69
  },
69
70
  closeOnEsc: { type: Boolean, default: true },
70
71
  disabled: { type: Boolean, default: false },
71
- width: { type: [String, Number], default: null },
72
+ width: { type: [String, Number], default: 280 },
72
73
  trigger: {
73
74
  type: Array,
74
75
  default: function () {
@@ -92,7 +93,8 @@ export default {
92
93
  },
93
94
  overlayClassName: { type: [String, Array, Object], default: null },
94
95
  overlayStyle: { type: [Object, Array], default: null },
95
- showChecked: { type: Boolean, default: false }
96
+ showChecked: { type: Boolean, default: false },
97
+ persistent: { type: Boolean, default: false }
96
98
  },
97
99
  emits: ['change', 'update:modelValue', 'open', 'close', 'select'],
98
100
  data() {
@@ -149,6 +151,10 @@ export default {
149
151
  },
150
152
  beforeUnmount() {
151
153
  if (this._hoverTimer) clearTimeout(this._hoverTimer)
154
+ },
155
+ // eslint-disable-next-line vue/no-deprecated-destroyed-lifecycle, vue/no-dupe-keys
156
+ beforeDestroy() {
157
+ if (this._hoverTimer) clearTimeout(this._hoverTimer)
152
158
  }
153
159
  }
154
160
  </script>
@@ -95,6 +95,7 @@
95
95
  /* Disabled — visual only; cursor handled by inner field. */
96
96
  .ui-input--disabled {
97
97
  opacity: 0.5;
98
+ background: var(--secondary-bg);
98
99
  }
99
100
 
100
101
  .ui-input--disabled:focus-within {
@@ -66,6 +66,7 @@ export default {
66
66
  closeOnSelect: { type: Boolean, default: true },
67
67
  closeOnEsc: { type: Boolean, default: true },
68
68
  disabled: { type: Boolean, default: false },
69
+ persistent: { type: Boolean, default: false },
69
70
  anchorWidth: { type: Boolean, default: false },
70
71
  width: { type: [String, Number], default: null },
71
72
  overlayClassName: { type: [String, Array, Object], default: null },
@@ -110,9 +111,9 @@ export default {
110
111
  zIndex: this.zIndex || null
111
112
  }
112
113
  if (this.anchorWidth && this.pmAnchorWidth) {
113
- style.minWidth = this.pmAnchorWidth + 'px'
114
+ style.width = this.pmAnchorWidth + 'px'
114
115
  } else if (this.width) {
115
- style.minWidth = typeof this.width === 'number' ? `${this.width}px` : this.width
116
+ style.width = typeof this.width === 'number' ? `${this.width}px` : this.width
116
117
  }
117
118
  return style
118
119
  }
@@ -146,6 +147,10 @@ export default {
146
147
  beforeUnmount() {
147
148
  this._cleanupAll()
148
149
  },
150
+ // eslint-disable-next-line vue/no-deprecated-destroyed-lifecycle, vue/no-dupe-keys
151
+ beforeDestroy() {
152
+ this._cleanupAll()
153
+ },
149
154
  methods: {
150
155
  setOpenState(v) {
151
156
  this.$emit('change', v)
@@ -210,6 +215,7 @@ export default {
210
215
  this.closeMenu()
211
216
  },
212
217
  _coOnOutside() {
218
+ if (this.persistent) return
213
219
  this.closeMenu()
214
220
  },
215
221
  _cleanupAll() {
@@ -19,7 +19,6 @@
19
19
  box-shadow: var(--shadow-md);
20
20
  display: flex;
21
21
  flex-direction: column;
22
- min-width: 176px;
23
22
  outline: none;
24
23
  pointer-events: auto;
25
24
  box-sizing: border-box;
@@ -1,25 +1,40 @@
1
1
  <template>
2
2
  <div class="ui-message" :class="`ui-message--${type}`" role="status" aria-live="polite">
3
- <span class="ui-message__icon">
4
- <WkSpinner v-if="type === 'loading'" size="sm" />
5
- <component :is="iconComponent" v-else :size="18" />
6
- </span>
7
- <div class="ui-message__wrapper">
8
- <span class="ui-message__content">
9
- <slot>{{ content }}</slot>
3
+ <div class="ui-message__row">
4
+ <div v-if="imgSrc" class="ui-message__file-upload__preview">
5
+ <img :src="imgSrc" alt="" />
6
+ </div>
7
+ <span v-else class="ui-message__icon">
8
+ <WkSpinner v-if="type === 'loading'" size="sm" />
9
+ <component :is="iconComponent" v-else :size="18" />
10
10
  </span>
11
- <span v-if="description" class="ui-message__description">{{ description }}</span>
11
+ <div class="ui-message__wrapper">
12
+ <span class="ui-message__content">
13
+ <slot>{{ content }}</slot>
14
+ </span>
15
+ <span v-if="description" class="ui-message__description">{{ description }}</span>
16
+ </div>
17
+ <WkButton
18
+ v-if="action"
19
+ size="mini"
20
+ :variant="progress ? 'ghost' : action.variant || 'primary'"
21
+ @click="handleAction"
22
+ :label="action.label"
23
+ >
24
+ <template #icon>
25
+ <WkiPause />
26
+ </template>
27
+ </WkButton>
12
28
  </div>
13
- <WkButton v-if="action" size="mini" :variant="action.variant || 'primary'" @click="handleAction">{{
14
- action.label
15
- }}</WkButton>
29
+ <WkProgress v-if="progress !== null" :value="progress" />
16
30
  </div>
17
31
  </template>
18
32
 
19
33
  <script>
20
34
  import WkSpinner from '../spinner/Spinner.vue'
21
35
  import WkButton from '../button/Button.vue'
22
- import { WkiCircleCheck, WkiCircleX, WkiTriangleAlert, WkiInfo } from '../../icons'
36
+ import WkProgress from '../progress/Progress.vue'
37
+ import { WkiCircleCheck, WkiCircleX, WkiTriangleAlert, WkiInfo, WkiPause } from '../../icons'
23
38
 
24
39
  const ICON_BY_TYPE = {
25
40
  success: 'WkiCircleCheck',
@@ -30,7 +45,7 @@ const ICON_BY_TYPE = {
30
45
 
31
46
  export default {
32
47
  name: 'Message',
33
- components: { WkSpinner, WkButton, WkiCircleCheck, WkiCircleX, WkiTriangleAlert, WkiInfo },
48
+ components: { WkSpinner, WkButton, WkProgress, WkiCircleCheck, WkiCircleX, WkiTriangleAlert, WkiInfo, WkiPause },
34
49
  props: {
35
50
  type: {
36
51
  type: String,
@@ -50,6 +65,14 @@ export default {
50
65
  action: {
51
66
  type: Object,
52
67
  default: null
68
+ },
69
+ progress: {
70
+ type: Number,
71
+ default: null
72
+ },
73
+ imgSrc: {
74
+ type: String,
75
+ default: ''
53
76
  }
54
77
  },
55
78
  emits: ['close'],
@@ -13,6 +13,8 @@
13
13
  :content="item.content"
14
14
  :description="item.description"
15
15
  :action="item.action"
16
+ :progress="item.progress"
17
+ :img-src="item.imgSrc"
16
18
  @close="remove(item.key)"
17
19
  />
18
20
  </div>
@@ -145,7 +147,9 @@ export default {
145
147
  existing.content = config.content != null ? config.content : ''
146
148
  existing.description = config.description || null
147
149
  existing.action = config.action || null
148
- existing.onClose = config.onClose || null
150
+ ;((existing.progress = config.progress != null ? config.progress : null),
151
+ (existing.imgSrc = config.imgSrc),
152
+ (existing.onClose = config.onClose || null))
149
153
  this.scheduleRemoval(key, config.duration)
150
154
  return key
151
155
  }
@@ -155,6 +159,8 @@ export default {
155
159
  content: config.content != null ? config.content : '',
156
160
  description: config.description || null,
157
161
  action: config.action || null,
162
+ progress: config.progress != null ? config.progress : null,
163
+ imgSrc: config.imgSrc,
158
164
  onClose: config.onClose || null
159
165
  })
160
166
  // Enforce maxCount by evicting the oldest entries.
@@ -1,7 +1,7 @@
1
1
  .ui-message {
2
2
  pointer-events: auto;
3
3
  display: inline-flex;
4
- align-items: center;
4
+ flex-direction: column;
5
5
  gap: var(--spacing-sm);
6
6
  max-width: 480px;
7
7
  padding: var(--spacing-sm) var(--spacing-md);
@@ -11,6 +11,12 @@
11
11
  box-shadow: var(--shadow-sonner);
12
12
  }
13
13
 
14
+ .ui-message__row {
15
+ display: flex;
16
+ align-items: center;
17
+ gap: var(--spacing-sm);
18
+ }
19
+
14
20
  .ui-message__icon {
15
21
  display: inline-flex;
16
22
  align-items: center;
@@ -35,6 +41,9 @@
35
41
  line-height: var(--paragraph-small-line-height);
36
42
  letter-spacing: var(--paragraph-small-letter-spacing, 0);
37
43
  word-break: break-word;
44
+ white-space: nowrap;
45
+ overflow: hidden;
46
+ text-overflow: ellipsis;
38
47
  }
39
48
 
40
49
  .ui-message__description {
@@ -46,6 +55,27 @@
46
55
  line-height: var(--paragraph-small-line-height);
47
56
  letter-spacing: var(--paragraph-small-letter-spacing);
48
57
  word-break: break-word;
58
+ -webkit-line-clamp: 2;
59
+ line-clamp: 2;
60
+ overflow: hidden;
61
+ }
62
+
63
+ .ui-message--file-upload {
64
+ width: 360px;
65
+ }
66
+
67
+ .ui-message__file-upload__preview {
68
+ width: 32px;
69
+ height: 32px;
70
+ border-radius: var(--rounded-sm);
71
+ overflow: hidden;
72
+ flex-shrink: 0;
73
+ }
74
+
75
+ .ui-message__file-upload__preview img {
76
+ width: 100%;
77
+ height: 100%;
78
+ object-fit: cover;
49
79
  }
50
80
 
51
81
  .ui-message--success .ui-message__icon {
@@ -87,6 +87,8 @@ function open(config) {
87
87
  content: config.content,
88
88
  description: config.description,
89
89
  action: config.action,
90
+ progress: config.progress != null ? config.progress : null,
91
+ imgSrc: config.imgSrc,
90
92
  duration,
91
93
  onClose: config.onClose
92
94
  })
@@ -24,8 +24,10 @@
24
24
  'ui-select--loading': loading,
25
25
  'ui-select--multiple': isMultiMode,
26
26
  'ui-select--searchable': isSearchable
27
- }
27
+ },
28
+ externalClass
28
29
  ]"
30
+ :style="externalStyle"
29
31
  :tabindex="isSearchable ? -1 : 0"
30
32
  v-bind="attrs"
31
33
  @click="isSearchable ? openIfClosed() : toggle()"
@@ -118,6 +120,8 @@ export default {
118
120
  return { select: this }
119
121
  },
120
122
 
123
+ inheritAttrs: false,
124
+
121
125
  model: {
122
126
  prop: 'value',
123
127
  event: 'change'
@@ -172,7 +176,7 @@ export default {
172
176
  default: false
173
177
  },
174
178
  listHeight: {
175
- type: Number,
179
+ type: [Number, String],
176
180
  default: 256
177
181
  },
178
182
  mode: {
@@ -278,7 +282,33 @@ export default {
278
282
  return !!((this.$scopedSlots && this.$scopedSlots['icon']) || this.$slots['icon'])
279
283
  },
280
284
  listStyle() {
281
- return { maxHeight: this.listHeight + 'px' }
285
+ if (typeof this.listHeight === 'number') return { maxHeight: this.listHeight + 'px' }
286
+ return { maxHeight: this.listHeight }
287
+ },
288
+ externalClass() {
289
+ // Vue 3: class is in $attrs (inheritAttrs:false keeps it off the root)
290
+ if (this.$attrs && Object.prototype.hasOwnProperty.call(this.$attrs, 'class')) {
291
+ return this.$attrs.class
292
+ }
293
+ // Vue 2: class always lands on root regardless of inheritAttrs; also bind here
294
+ if (this.$vnode && this.$vnode.data) {
295
+ var parts = []
296
+ if (this.$vnode.data.staticClass) parts.push(this.$vnode.data.staticClass)
297
+ if (this.$vnode.data.class) parts.push(this.$vnode.data.class)
298
+ return parts.length ? parts : null
299
+ }
300
+ return null
301
+ },
302
+ externalStyle() {
303
+ // Vue 3: style is in $attrs
304
+ if (this.$attrs && Object.prototype.hasOwnProperty.call(this.$attrs, 'style')) {
305
+ return this.$attrs.style
306
+ }
307
+ // Vue 2: style always lands on root; also bind here for consistency
308
+ if (this.$vnode && this.$vnode.data) {
309
+ return this.$vnode.data.staticStyle || this.$vnode.data.style || null
310
+ }
311
+ return null
282
312
  }
283
313
  },
284
314
 
@@ -394,9 +424,16 @@ export default {
394
424
  }
395
425
  },
396
426
  registerOption(value, label) {
427
+ // Guard: SelectOption.updated() calls this on every re-render. Reassigning
428
+ // labelCache to a new object each time invalidates dependent computeds
429
+ // (selectedLabel, option.isHidden), forcing another render → updated() →
430
+ // registerOption → … an infinite loop that lags typing in searchable mode.
431
+ // Skip when the label is unchanged so the reference stays stable.
432
+ if (this.labelCache[value] === label) return
397
433
  this.labelCache = Object.assign({}, this.labelCache, { [value]: label })
398
434
  },
399
435
  setSlotOptionVisible(value, visible) {
436
+ if (this.slotOptionVisible[value] === visible) return
400
437
  this.slotOptionVisible = Object.assign({}, this.slotOptionVisible, { [value]: visible })
401
438
  },
402
439
  removeSlotOption(value) {
@@ -85,6 +85,10 @@ export default {
85
85
  if (this.select) this.select.removeSlotOption(this.value)
86
86
  },
87
87
 
88
+ // eslint-disable-next-line vue/no-deprecated-destroyed-lifecycle, vue/no-dupe-keys
89
+ beforeDestroy() {
90
+ if (this.select) this.select.removeSlotOption(this.value)
91
+ },
88
92
  watch: {
89
93
  isHidden(val) {
90
94
  if (this.select) this.select.setSlotOptionVisible(this.value, !val)
@@ -4,7 +4,8 @@
4
4
  :class="{
5
5
  'ui-slider--vertical': orientation === 'vertical',
6
6
  'ui-slider--disabled': disabled,
7
- 'ui-slider--range': range
7
+ 'ui-slider--range': range,
8
+ [`ui-slider--${variant}`]: variant
8
9
  }"
9
10
  :style="rootStyle"
10
11
  >
@@ -88,6 +89,13 @@ export default {
88
89
  return ['horizontal', 'vertical'].indexOf(v) !== -1
89
90
  }
90
91
  },
92
+ variant: {
93
+ type: String,
94
+ default: 'neutral',
95
+ validator: function (v) {
96
+ return ['neutral', 'primary'].indexOf(v) !== -1
97
+ }
98
+ },
91
99
  disabled: { type: Boolean, default: false },
92
100
  width: { type: [String, Number], default: null },
93
101
  stepRanges: {
@@ -191,6 +199,10 @@ export default {
191
199
  beforeUnmount: function () {
192
200
  this.cleanup()
193
201
  },
202
+ // eslint-disable-next-line vue/no-deprecated-destroyed-lifecycle, vue/no-dupe-keys
203
+ beforeDestroy() {
204
+ this.cleanup()
205
+ },
194
206
  methods: {
195
207
  cleanup: function () {
196
208
  document.removeEventListener('mousemove', this.boundMouseMove)
@@ -45,6 +45,10 @@
45
45
  pointer-events: none;
46
46
  }
47
47
 
48
+ .ui-slider--neutral .ui-slider__fill {
49
+ background: var(--primary-fg);
50
+ }
51
+
48
52
  .ui-slider--vertical .ui-slider__fill {
49
53
  top: auto;
50
54
  left: 0;
@@ -67,6 +71,10 @@
67
71
  transition: box-shadow 0.1s ease;
68
72
  }
69
73
 
74
+ .ui-slider--neutral .ui-slider__thumb {
75
+ background: var(--primary-fg);
76
+ }
77
+
70
78
  .ui-slider--vertical .ui-slider__thumb {
71
79
  top: auto;
72
80
  left: 50%;
@@ -79,6 +87,12 @@
79
87
  0 0 0 5px color-mix(in srgb, var(--primary-brand-bg) 20%, transparent);
80
88
  }
81
89
 
90
+ .ui-slider--neutral .ui-slider__thumb:hover {
91
+ box-shadow:
92
+ var(--shadow-xs),
93
+ 0 0 0 5px color-mix(in srgb, var(--primary-fg) 20%, transparent);
94
+ }
95
+
82
96
  .ui-slider__thumb:focus-visible {
83
97
  box-shadow: var(--shadow-xs), var(--shadow-focus-ring);
84
98
  }
@@ -1,5 +1,8 @@
1
1
  <template>
2
- <span class="ui-tag" :class="[`ui-tag--type-${type}`, `ui-tag--${size}`, { 'ui-tag--with-icon': hasIcon }]">
2
+ <span
3
+ :class="['ui-tag', `ui-tag--type-${type}`, `ui-tag--${size}`, { 'ui-tag--with-icon': hasIcon }]"
4
+ @click="handleClick"
5
+ >
3
6
  <span v-if="hasIcon" class="ui-tag__icon"><slot name="icon" /></span>
4
7
  <span class="ui-tag__label"
5
8
  ><slot>{{ label }}</slot></span
@@ -35,11 +38,16 @@ export default {
35
38
  closable: { type: Boolean, default: false },
36
39
  label: { type: String, default: '' }
37
40
  },
38
- emits: ['close'],
41
+ emits: ['close', 'click'],
39
42
  computed: {
40
43
  hasIcon: function () {
41
44
  return !!((this.$scopedSlots && this.$scopedSlots['icon']) || this.$slots['icon'])
42
45
  }
46
+ },
47
+ methods: {
48
+ handleClick(e) {
49
+ this.$emit('click', e)
50
+ }
43
51
  }
44
52
  }
45
53
  </script>
@@ -132,7 +132,6 @@
132
132
  }
133
133
  .ui-toggle--variant-outlined.ui-toggle--active {
134
134
  background: var(--outline-active);
135
- color: var(--primary-fg);
136
135
  }
137
136
 
138
137
  /* ============================================================
@@ -165,6 +164,14 @@
165
164
  pointer-events: none;
166
165
  }
167
166
 
167
+ .ui-toggle--variant-outlined:disabled {
168
+ background: var(--secondary-bg);
169
+ }
170
+
171
+ .ui-toggle--variant-outlined.ui-toggle--active:disabled {
172
+ background: var(--tertiary-bg);
173
+ }
174
+
168
175
  /* ============================================================
169
176
  Icon-only — square button per size
170
177
  ============================================================ */
@@ -26,7 +26,12 @@
26
26
  <span class="ui-tooltip__text">
27
27
  <slot name="content">{{ title }}</slot>
28
28
  </span>
29
- <span v-if="arrow" :class="['ui-tooltip__arrow', `ui-tooltip__arrow--${side}`]" aria-hidden="true"></span>
29
+ <span
30
+ v-if="arrow"
31
+ :class="['ui-tooltip__arrow', `ui-tooltip__arrow--${side}`]"
32
+ :style="arrowStyle"
33
+ aria-hidden="true"
34
+ ></span>
30
35
  </span>
31
36
  </span>
32
37
  </template>
@@ -36,6 +41,7 @@ import { acquire, release } from '../../floating/portal-root.js'
36
41
  import { nextZIndex } from '../../floating/layer-manager.js'
37
42
 
38
43
  const GAP = 4
44
+ const EDGE_PAD = 4
39
45
 
40
46
  export default {
41
47
  name: 'Tooltip',
@@ -61,12 +67,14 @@ export default {
61
67
  data() {
62
68
  return {
63
69
  hovered: false,
64
- positionStyle: {}
70
+ triggerInView: true,
71
+ positionStyle: {},
72
+ arrowStyle: {}
65
73
  }
66
74
  },
67
75
  computed: {
68
76
  isVisible() {
69
- return this.open || this.hovered
77
+ return (this.open || this.hovered) && this.triggerInView
70
78
  },
71
79
  hasMaxWidth() {
72
80
  return this.maxWidth !== '' && this.maxWidth !== null && this.maxWidth !== undefined
@@ -81,22 +89,42 @@ export default {
81
89
  },
82
90
  watch: {
83
91
  isVisible(v) {
84
- if (v) this.$nextTick(this.updatePosition)
92
+ if (v) {
93
+ this.$nextTick(() => {
94
+ this._mountToPortal()
95
+ this.updatePosition()
96
+ })
97
+ } else {
98
+ this._unmountFromPortal()
99
+ }
85
100
  },
86
101
  side() {
87
102
  if (this.isVisible) this.$nextTick(this.updatePosition)
88
103
  }
89
104
  },
90
105
  mounted() {
91
- if (typeof document !== 'undefined' && this.$refs.tooltip) {
92
- this._tooltipPortalRoot = acquire()
93
- if (this._tooltipPortalRoot) {
94
- this._tooltipPortalRoot.appendChild(this.$refs.tooltip)
95
- }
106
+ const tip = this.$refs.tooltip
107
+ if (tip) {
108
+ this._originalParent = tip.parentNode
109
+ this._originalNextSibling = tip.nextSibling
110
+ }
111
+ if (typeof IntersectionObserver !== 'undefined' && this.$refs.trigger) {
112
+ this._triggerObserver = new IntersectionObserver(
113
+ ([entry]) => {
114
+ this.triggerInView = entry.isIntersecting
115
+ },
116
+ { threshold: 0 }
117
+ )
118
+ this._triggerObserver.observe(this.$refs.trigger)
96
119
  }
97
120
  window.addEventListener('scroll', this.onScrollOrResize, true)
98
121
  window.addEventListener('resize', this.onScrollOrResize)
99
- if (this.open) this.$nextTick(this.updatePosition)
122
+ if (this.open) {
123
+ this.$nextTick(() => {
124
+ this._mountToPortal()
125
+ this.updatePosition()
126
+ })
127
+ }
100
128
  if (typeof this.$on === 'function') {
101
129
  // eslint-disable-next-line vue/no-deprecated-events-api
102
130
  this.$on('hook:beforeDestroy', this.cleanup)
@@ -105,6 +133,10 @@ export default {
105
133
  beforeUnmount() {
106
134
  this.cleanup()
107
135
  },
136
+ // eslint-disable-next-line vue/no-deprecated-destroyed-lifecycle, vue/no-dupe-keys
137
+ beforeDestroy() {
138
+ this.cleanup()
139
+ },
108
140
  methods: {
109
141
  onShow() {
110
142
  this.hovered = true
@@ -137,6 +169,17 @@ export default {
137
169
  top = t.top + (t.height - th) / 2
138
170
  left = t.right + GAP
139
171
  }
172
+ const vw = window.innerWidth
173
+ const vh = window.innerHeight
174
+ left = Math.max(EDGE_PAD, Math.min(left, vw - tw - EDGE_PAD))
175
+ top = Math.max(EDGE_PAD, Math.min(top, vh - th - EDGE_PAD))
176
+
177
+ if (this.side === 'top' || this.side === 'bottom') {
178
+ this.arrowStyle = { left: t.left + t.width / 2 - left + 'px' }
179
+ } else {
180
+ this.arrowStyle = { top: t.top + t.height / 2 - top + 'px' }
181
+ }
182
+
140
183
  this.positionStyle = {
141
184
  position: 'fixed',
142
185
  top: top + 'px',
@@ -144,11 +187,35 @@ export default {
144
187
  zIndex: nextZIndex()
145
188
  }
146
189
  },
190
+ _mountToPortal() {
191
+ const tip = this.$refs.tooltip
192
+ if (typeof document === 'undefined' || !tip) return
193
+ if (!this._tooltipPortalRoot) {
194
+ this._tooltipPortalRoot = acquire()
195
+ }
196
+ if (this._tooltipPortalRoot && tip.parentNode !== this._tooltipPortalRoot) {
197
+ this._tooltipPortalRoot.appendChild(tip)
198
+ }
199
+ },
200
+ _unmountFromPortal() {
201
+ const tip = this.$refs.tooltip
202
+ if (!tip || !this._tooltipPortalRoot) return
203
+ if (tip.parentNode === this._tooltipPortalRoot && this._originalParent) {
204
+ this._originalParent.insertBefore(tip, this._originalNextSibling || null)
205
+ }
206
+ release()
207
+ this._tooltipPortalRoot = null
208
+ },
147
209
  cleanup() {
210
+ if (this._triggerObserver) {
211
+ this._triggerObserver.disconnect()
212
+ this._triggerObserver = null
213
+ }
148
214
  window.removeEventListener('scroll', this.onScrollOrResize, true)
149
215
  window.removeEventListener('resize', this.onScrollOrResize)
150
- if (this.$refs.tooltip && this.$refs.tooltip.parentNode) {
151
- this.$refs.tooltip.parentNode.removeChild(this.$refs.tooltip)
216
+ const tip = this.$refs.tooltip
217
+ if (tip && tip.parentNode && tip.parentNode !== this._originalParent) {
218
+ tip.parentNode.removeChild(tip)
152
219
  }
153
220
  if (this._tooltipPortalRoot) {
154
221
  release()
@@ -0,0 +1,48 @@
1
+ <template>
2
+ <BaseIcon v-bind="$attrs" v-on="listeners">
3
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
4
+ <path
5
+ d="M5.17284 14C5.40204 14 5.62227 14.0905 5.78434 14.2524C5.9464 14.4142 6.03704 14.6341 6.03704 14.863V16.9589H8.1358C8.365 16.9589 8.58523 17.0494 8.7473 17.2113C8.90937 17.3731 9 17.593 9 17.8219C9 18.0508 8.90937 18.2707 8.7473 18.4326C8.58523 18.5944 8.365 18.6849 8.1358 18.6849H5.17284C4.94364 18.6849 4.72341 18.5944 4.56134 18.4326C4.39927 18.2707 4.30864 18.0508 4.30864 17.8219V14.863C4.30864 14.6341 4.39927 14.4142 4.56134 14.2524C4.72341 14.0905 4.94364 14 5.17284 14Z"
6
+ fill="currentColor"
7
+ />
8
+ <path
9
+ d="M18.8272 5.31507C19.0564 5.31507 19.2766 5.40558 19.4387 5.56742C19.6007 5.72927 19.6914 5.9492 19.6914 6.17808V9.13699C19.6914 9.36587 19.6007 9.5858 19.4387 9.74765C19.2766 9.90949 19.0564 10 18.8272 10C18.598 10 18.3777 9.90949 18.2157 9.74765C18.0536 9.5858 17.963 9.36587 17.963 9.13699V7.0411H15.8642C15.635 7.0411 15.4148 6.95059 15.2527 6.78874C15.0906 6.62689 15 6.40697 15 6.17808C15 5.9492 15.0906 5.72927 15.2527 5.56742C15.4148 5.40558 15.635 5.31507 15.8642 5.31507H18.8272Z"
10
+ fill="currentColor"
11
+ />
12
+ <path
13
+ fill-rule="evenodd"
14
+ clip-rule="evenodd"
15
+ d="M20.3951 3C20.8207 3 21.2293 3.1685 21.5303 3.46907C21.8313 3.76964 22 4.17767 22 4.60274V19.3973C22 19.8223 21.8313 20.2304 21.5303 20.5309C21.2293 20.8315 20.8207 21 20.3951 21H3.60494C3.17928 21 2.7707 20.8315 2.46971 20.5309C2.16873 20.2304 2 19.8223 2 19.3973V4.60274C2 4.17767 2.16873 3.76964 2.46971 3.46907C2.7707 3.1685 3.17928 3 3.60494 3H20.3951ZM3.7284 19.274H20.2716V4.72603H3.7284V19.274Z"
16
+ fill="currentColor"
17
+ />
18
+ <path
19
+ d="M12.1281 12.8517L12.8096 12.3073C13.1389 12.0442 13.1882 11.5689 12.9198 11.2456C12.6513 10.9224 12.1667 10.8736 11.8374 11.1367L11.156 11.6811C10.8267 11.9441 10.7774 12.4195 11.0458 12.7427C11.3143 13.066 11.7988 13.1148 12.1281 12.8517Z"
20
+ fill="currentColor"
21
+ />
22
+ <path
23
+ d="M8.72096 15.5736L9.4024 15.0292C9.7317 14.7662 9.78103 14.2909 9.51258 13.9676C9.24413 13.6443 8.75956 13.5955 8.43025 13.8586L7.74882 14.403C7.41951 14.6661 7.37018 15.1414 7.63863 15.4647C7.90708 15.7879 8.39165 15.8367 8.72096 15.5736Z"
24
+ fill="currentColor"
25
+ />
26
+ <path
27
+ d="M15.5353 10.1297L16.2168 9.58535C16.5461 9.32228 16.5954 8.84696 16.327 8.5237C16.0585 8.20044 15.5739 8.15165 15.2446 8.41472L14.5632 8.95911C14.2339 9.22219 14.1846 9.69751 14.453 10.0208C14.7215 10.344 15.206 10.3928 15.5353 10.1297Z"
28
+ fill="currentColor"
29
+ />
30
+ </svg>
31
+ </BaseIcon>
32
+ </template>
33
+
34
+ <script>
35
+ import BaseIcon from './BaseIcon.vue'
36
+
37
+ export default {
38
+ name: 'FrameCornersDash',
39
+ components: { BaseIcon },
40
+ inheritAttrs: false,
41
+ emits: [],
42
+ computed: {
43
+ listeners() {
44
+ return this.$listeners || {}
45
+ }
46
+ }
47
+ }
48
+ </script>
@@ -0,0 +1,68 @@
1
+ <template>
2
+ <BaseIcon v-bind="$attrs" v-on="listeners">
3
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
4
+ <path
5
+ d="M4.96582 13.6934C5.56831 13.6934 6.05664 14.1817 6.05664 14.7842C6.05659 15.3866 5.56828 15.875 4.96582 15.875H4.9541C4.35164 15.875 3.86333 15.3866 3.86328 14.7842C3.86328 14.1817 4.35161 13.6934 4.9541 13.6934H4.96582Z"
6
+ fill="currentColor"
7
+ />
8
+ <path
9
+ d="M8.25098 13.6934C8.85339 13.6934 9.3418 14.1817 9.3418 14.7842C9.34175 15.3866 8.85336 15.8749 8.25098 15.875H8.23828C7.63598 15.8748 7.14751 15.3865 7.14746 14.7842C7.14746 14.1818 7.63595 13.6936 8.23828 13.6934H8.25098Z"
10
+ fill="currentColor"
11
+ />
12
+ <path
13
+ d="M11.5117 13.6934C12.1141 13.6935 12.6025 14.1818 12.6025 14.7842C12.6025 15.3865 12.114 15.8748 11.5117 15.875H11.499C10.8966 15.8749 10.4083 15.3866 10.4082 14.7842C10.4082 14.1817 10.8966 13.6935 11.499 13.6934H11.5117Z"
14
+ fill="currentColor"
15
+ />
16
+ <path
17
+ d="M14.7842 13.6934C15.3866 13.6934 15.875 14.1817 15.875 14.7842C15.875 15.3866 15.3866 15.875 14.7842 15.875H14.7715C14.1692 15.8748 13.6807 15.3865 13.6807 14.7842C13.6807 14.1818 14.1692 13.6936 14.7715 13.6934H14.7842Z"
18
+ fill="currentColor"
19
+ />
20
+ <path
21
+ d="M8.25098 10.4209C8.85339 10.421 9.3418 10.9093 9.3418 11.5117C9.34161 12.114 8.85327 12.6024 8.25098 12.6025H8.23828C7.63607 12.6023 7.14765 12.1139 7.14746 11.5117C7.14746 10.9093 7.63595 10.4211 8.23828 10.4209H8.25098Z"
22
+ fill="currentColor"
23
+ />
24
+ <path
25
+ d="M11.5234 10.4209C12.1259 10.4209 12.6143 10.9092 12.6143 11.5117C12.6141 12.114 12.1258 12.6025 11.5234 12.6025H11.5117C10.9093 12.6025 10.4211 12.114 10.4209 11.5117C10.4209 10.9092 10.9092 10.4209 11.5117 10.4209H11.5234Z"
26
+ fill="currentColor"
27
+ />
28
+ <path
29
+ d="M14.7842 10.4209C15.3866 10.4209 15.875 10.9093 15.875 11.5117C15.8748 12.114 15.3865 12.6025 14.7842 12.6025H14.7715C14.1693 12.6023 13.6809 12.1139 13.6807 11.5117C13.6807 10.9094 14.1692 10.4211 14.7715 10.4209H14.7842Z"
30
+ fill="currentColor"
31
+ />
32
+ <path
33
+ d="M11.5117 7.14746C12.1139 7.14765 12.6023 7.63607 12.6025 8.23828C12.6025 8.84065 12.114 9.32891 11.5117 9.3291H11.499C10.8966 9.32901 10.4082 8.84072 10.4082 8.23828C10.4084 7.63601 10.8967 7.14755 11.499 7.14746H11.5117Z"
34
+ fill="currentColor"
35
+ />
36
+ <path
37
+ d="M14.7842 7.14746C15.3865 7.14751 15.8748 7.63598 15.875 8.23828C15.875 8.84074 15.3866 9.32905 14.7842 9.3291H14.7715C14.1692 9.32887 13.6807 8.84063 13.6807 8.23828C13.6809 7.6361 14.1693 7.14769 14.7715 7.14746H14.7842Z"
38
+ fill="currentColor"
39
+ />
40
+ <path
41
+ d="M14.7842 3.875C15.3866 3.87505 15.875 4.3634 15.875 4.96582C15.875 5.56828 15.3866 6.05659 14.7842 6.05664H14.7715C14.1692 6.05641 13.6807 5.56817 13.6807 4.96582C13.6807 4.36351 14.1692 3.87523 14.7715 3.875H14.7842Z"
42
+ fill="currentColor"
43
+ />
44
+ <path
45
+ fill-rule="evenodd"
46
+ clip-rule="evenodd"
47
+ d="M16.875 0C18.4628 0 19.75 1.28718 19.75 2.875V16.875C19.75 18.4628 18.4628 19.75 16.875 19.75H2.875C1.28718 19.75 0 18.4628 0 16.875V2.875C0 1.28718 1.28718 0 2.875 0H16.875ZM2.875 1.75C2.25368 1.75 1.75 2.25368 1.75 2.875V16.875C1.75 17.4963 2.25368 18 2.875 18H16.875C17.4963 18 18 17.4963 18 16.875V2.875C18 2.25368 17.4963 1.75 16.875 1.75H2.875Z"
48
+ fill="currentColor"
49
+ />
50
+ </svg>
51
+ </BaseIcon>
52
+ </template>
53
+
54
+ <script>
55
+ import BaseIcon from './BaseIcon.vue'
56
+
57
+ export default {
58
+ name: 'Opacity',
59
+ components: { BaseIcon },
60
+ inheritAttrs: false,
61
+ emits: [],
62
+ computed: {
63
+ listeners() {
64
+ return this.$listeners || {}
65
+ }
66
+ }
67
+ }
68
+ </script>
@@ -1742,5 +1742,7 @@ export { default as WkiSquareNumber } from './SquareNumber.vue'
1742
1742
  export { default as WkiRows3Add } from './Rows3Add.vue'
1743
1743
  export { default as WkiColumns3Add } from './Columns3Add.vue'
1744
1744
  export { default as WkiFrameCorners } from './FrameCorners.vue'
1745
+ export { default as WkiFrameCornersDash } from './FrameCornersDash.vue'
1746
+ export { default as WkiOpacity } from './Opacity.vue'
1745
1747
 
1746
1748
  // --- AUTO-GENERATED LUCIDE EXPORTS END ---
@@ -15,8 +15,8 @@
15
15
 
16
16
  /* ========================= Heading 2 ========================= */
17
17
  --heading-2-font-family: var(--font-family-heading);
18
- --heading-2-font-size: 30px;
19
- --heading-2-line-height: 30px;
18
+ --heading-2-font-size: 32px;
19
+ --heading-2-line-height: 40px;
20
20
  --heading-2-font-weight: 600;
21
21
  --heading-2-letter-spacing: -1px;
22
22