@vialiq/flux-ui 0.19.0 → 0.21.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.
- package/README.md +22 -0
- package/components/_accordion.scss +13 -13
- package/components/_alert.scss +7 -7
- package/components/_animation.scss +3 -3
- package/components/_badge.scss +8 -6
- package/components/_checkbox.scss +5 -4
- package/components/_chip.scss +4 -4
- package/components/_index.scss +5 -1
- package/components/_label.scss +3 -3
- package/components/_modal.scss +7 -5
- package/components/_radio.scss +7 -6
- package/components/_select.scss +6 -5
- package/components/_switch.scss +12 -11
- package/components/_tab-panel.scss +20 -0
- package/components/_tab.scss +213 -0
- package/components/_tabs.scss +574 -0
- package/components/_tag.scss +49 -66
- package/components/_tooltip.scss +10 -8
- package/flux-ui.css +1 -1
- package/flux-ui.css.map +1 -1
- package/package.json +1 -1
- package/styles/_flatpickr-core.scss +8 -7
- package/styles/_functions.scss +39 -0
- package/styles/_index.scss +1 -0
- package/styles/_layout.scss +1 -0
- package/styles/_utilities.scss +1 -1
- package/styles/_variables.scss +33 -37
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
@use '@vialiq/flux-ui/styles/variables' as tokens;
|
|
2
|
+
@use '../styles/functions' as func;
|
|
3
|
+
|
|
4
|
+
@layer components {
|
|
5
|
+
|
|
6
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
7
|
+
// vi-tabs — Container, tablist, SLIDING CURSOR, indicator, all variants
|
|
8
|
+
//
|
|
9
|
+
// Architecture:
|
|
10
|
+
// • .vi-tabs__cursor → absolutely positioned, slides between tabs (JS-positioned)
|
|
11
|
+
// Used by: pill, card, enclosed (the "selected surface")
|
|
12
|
+
// • .vi-tabs__indicator → absolutely positioned thin line at bottom/side (JS-positioned)
|
|
13
|
+
// Used by: line, secondary (the "underline signal")
|
|
14
|
+
//
|
|
15
|
+
// The cursor element ALWAYS exists in the DOM. CSS on each variant controls
|
|
16
|
+
// whether it's visible (opacity, border-radius, shadow etc.)
|
|
17
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
.vi-tabs {
|
|
21
|
+
display: grid;
|
|
22
|
+
grid-template-columns: minmax(0, 1fr);
|
|
23
|
+
position: relative;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.vi-tabs__scroll-btn {
|
|
27
|
+
grid-area: 1 / 1;
|
|
28
|
+
align-self: stretch;
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
justify-content: center;
|
|
32
|
+
width: 48px; // Wider to accommodate the gradient safely
|
|
33
|
+
z-index: 10;
|
|
34
|
+
border: none;
|
|
35
|
+
color: var(--vi-tabs-text-color, #{tokens.$text-primary});
|
|
36
|
+
cursor: pointer;
|
|
37
|
+
transition: opacity 0.2s;
|
|
38
|
+
|
|
39
|
+
&[disabled] {
|
|
40
|
+
opacity: 0;
|
|
41
|
+
pointer-events: none;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
&:hover, &:focus-visible {
|
|
45
|
+
color: var(--vi-tabs-primary-color, #{tokens.$color-primary});
|
|
46
|
+
outline: none;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
&--left {
|
|
50
|
+
justify-self: start;
|
|
51
|
+
background: linear-gradient(to right, var(--vi-tabs-surface-primary, #{tokens.$layer-01}) 50%, transparent 100%);
|
|
52
|
+
padding-right: 12px; // Push icon slightly left
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
&--right {
|
|
56
|
+
justify-self: end;
|
|
57
|
+
background: linear-gradient(to left, var(--vi-tabs-surface-primary, #{tokens.$layer-01}) 50%, transparent 100%);
|
|
58
|
+
padding-left: 12px; // Push icon slightly right
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
65
|
+
// Add Button
|
|
66
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
.vi-tabs__add-btn {
|
|
69
|
+
display: flex;
|
|
70
|
+
align-items: center;
|
|
71
|
+
justify-content: center;
|
|
72
|
+
width: 48px;
|
|
73
|
+
height: 48px;
|
|
74
|
+
flex-shrink: 0;
|
|
75
|
+
background: transparent;
|
|
76
|
+
border: none;
|
|
77
|
+
border-radius: 50%;
|
|
78
|
+
color: var(--vi-tabs-add-btn-color, #{tokens.$text-secondary});
|
|
79
|
+
cursor: pointer;
|
|
80
|
+
margin-left: 4px;
|
|
81
|
+
outline: none;
|
|
82
|
+
position: relative;
|
|
83
|
+
transition: background-color 0.2s, color 0.2s;
|
|
84
|
+
order: 9998; // Ensure it stays right before the More menu
|
|
85
|
+
|
|
86
|
+
&::before {
|
|
87
|
+
content: '';
|
|
88
|
+
position: absolute;
|
|
89
|
+
inset: 4px;
|
|
90
|
+
border-radius: 50%;
|
|
91
|
+
background-color: currentColor;
|
|
92
|
+
opacity: 0;
|
|
93
|
+
transition: opacity 0.2s;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
&:hover::before,
|
|
97
|
+
&:focus-visible::before {
|
|
98
|
+
opacity: 0.08;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
&:active::before {
|
|
102
|
+
opacity: 0.12;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
&:focus-visible {
|
|
106
|
+
border-radius: #{func.to-rem(4px)}; // Square focus ring for accessibility
|
|
107
|
+
outline: 2px solid var(--vi-tabs-focus-ring, #{tokens.$color-primary});
|
|
108
|
+
outline-offset: 2px;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
svg {
|
|
112
|
+
position: relative; // Above the hover state
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
117
|
+
// Shared tablist base
|
|
118
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
.vi-tabs__tablist {
|
|
121
|
+
grid-area: 1 / 1;
|
|
122
|
+
position: relative;
|
|
123
|
+
display: flex;
|
|
124
|
+
flex-direction: row;
|
|
125
|
+
align-items: stretch;
|
|
126
|
+
gap: 0;
|
|
127
|
+
// overflow: visible so overflow-menu dropdown is not clipped.
|
|
128
|
+
// Each variant overrides this as needed.
|
|
129
|
+
overflow: visible;
|
|
130
|
+
min-width: 0;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
134
|
+
// SLIDING CURSOR — the magic that makes this feel premium
|
|
135
|
+
//
|
|
136
|
+
// Positioned absolutely by JS. Each variant styles it differently.
|
|
137
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
.vi-tabs__cursor {
|
|
140
|
+
position: absolute;
|
|
141
|
+
top: 0;
|
|
142
|
+
left: 0;
|
|
143
|
+
pointer-events: none;
|
|
144
|
+
opacity: 0;
|
|
145
|
+
z-index: 0;
|
|
146
|
+
|
|
147
|
+
// Transitions — fast out slow in, matching motion system
|
|
148
|
+
transition:
|
|
149
|
+
left 220ms cubic-bezier(0.4, 0, 0.2, 1),
|
|
150
|
+
top 220ms cubic-bezier(0.4, 0, 0.2, 1),
|
|
151
|
+
width 220ms cubic-bezier(0.4, 0, 0.2, 1),
|
|
152
|
+
height 220ms cubic-bezier(0.4, 0, 0.2, 1),
|
|
153
|
+
opacity 120ms ease;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
157
|
+
// BOTTOM-LINE INDICATOR — for line / secondary variants
|
|
158
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
159
|
+
|
|
160
|
+
.vi-tabs__indicator {
|
|
161
|
+
position: absolute;
|
|
162
|
+
bottom: 0;
|
|
163
|
+
left: 0;
|
|
164
|
+
height: var(--vi-tabs-indicator-thickness, 2px);
|
|
165
|
+
width: 0;
|
|
166
|
+
background: var(--vi-tabs-indicator-color, #{tokens.$color-primary});
|
|
167
|
+
border-radius: #{func.to-rem(2px)} #{func.to-rem(2px)} 0 0;
|
|
168
|
+
opacity: 0;
|
|
169
|
+
pointer-events: none;
|
|
170
|
+
z-index: 2;
|
|
171
|
+
|
|
172
|
+
transition:
|
|
173
|
+
left 220ms cubic-bezier(0.4, 0, 0.2, 1),
|
|
174
|
+
width 220ms cubic-bezier(0.4, 0, 0.2, 1),
|
|
175
|
+
top 220ms cubic-bezier(0.4, 0, 0.2, 1),
|
|
176
|
+
height 220ms cubic-bezier(0.4, 0, 0.2, 1),
|
|
177
|
+
opacity 120ms ease;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
181
|
+
// VARIANT: line (default) — clean underline, minimal strip
|
|
182
|
+
//
|
|
183
|
+
// Design reference: Vercel, Linear, GitHub navigation
|
|
184
|
+
// Strip: barely-there border, no bg. Typography + indicator do the work.
|
|
185
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
186
|
+
|
|
187
|
+
.vi-tabs--line {
|
|
188
|
+
.vi-tabs__tablist {
|
|
189
|
+
border-bottom: 1px solid var(--vi-tabs-border-color, #{tokens.$color-grey-200});
|
|
190
|
+
background: transparent;
|
|
191
|
+
padding: 0;
|
|
192
|
+
gap: 0;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Cursor hidden — line variant uses indicator only
|
|
196
|
+
.vi-tabs__cursor {
|
|
197
|
+
display: none;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Indicator: solid 2px line
|
|
201
|
+
.vi-tabs__indicator {
|
|
202
|
+
bottom: -1px;
|
|
203
|
+
height: 2px;
|
|
204
|
+
background: var(--vi-tabs-primary-color, #{tokens.$color-primary});
|
|
205
|
+
border-radius: #{func.to-rem(2px)} #{func.to-rem(2px)} 0 0;
|
|
206
|
+
box-shadow: 0 0 0 0 transparent; // no glow on line variant — keep clean
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
211
|
+
// VARIANT: pill — segmented control / HeroUI / shadcn style
|
|
212
|
+
//
|
|
213
|
+
// Design reference: Apple iOS, shadcn/ui, HeroUI
|
|
214
|
+
// The cursor IS the active pill — white card that slides between tabs.
|
|
215
|
+
// Container: inset-shadow tray. Active: white card with shadow.
|
|
216
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
217
|
+
|
|
218
|
+
.vi-tabs--pill {
|
|
219
|
+
.vi-tabs__tablist {
|
|
220
|
+
background: var(--vi-tabs-pill-bg, #{tokens.$color-grey-100});
|
|
221
|
+
border: 1px solid var(--vi-tabs-border-color, #{tokens.$color-border});
|
|
222
|
+
border-radius: #{func.to-rem(10px)};
|
|
223
|
+
padding: var(--vi-tabs-spacing-xs, 4px);
|
|
224
|
+
gap: var(--vi-tabs-gap-xs, 2px);
|
|
225
|
+
// Inset shadow creates a "recessed tray" feel
|
|
226
|
+
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.07), inset 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// The sliding white pill cursor
|
|
230
|
+
.vi-tabs__cursor {
|
|
231
|
+
display: block;
|
|
232
|
+
inset: 4px auto; // matches tablist padding
|
|
233
|
+
border-radius: #{func.to-rem(7px)};
|
|
234
|
+
background: var(--vi-tabs-surface-primary, #{tokens.$layer-01});
|
|
235
|
+
// Multi-layer shadow for floating card feel
|
|
236
|
+
box-shadow:
|
|
237
|
+
0 1px 3px rgba(0, 0, 0, 0.10),
|
|
238
|
+
0 1px 2px rgba(0, 0, 0, 0.06),
|
|
239
|
+
0 0 0 1px rgba(0, 0, 0, 0.04);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Indicator hidden — cursor does selection work
|
|
243
|
+
.vi-tabs__indicator {
|
|
244
|
+
display: none;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
249
|
+
// VARIANT: secondary (M3 — sub-hierarchy, thinner indicator)
|
|
250
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
251
|
+
|
|
252
|
+
.vi-tabs--secondary {
|
|
253
|
+
.vi-tabs__tablist {
|
|
254
|
+
border-bottom: 1px solid var(--vi-tabs-border-color, #{tokens.$color-border});
|
|
255
|
+
background: transparent;
|
|
256
|
+
padding: 0;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.vi-tabs__cursor { display: none; }
|
|
260
|
+
|
|
261
|
+
.vi-tabs__indicator {
|
|
262
|
+
bottom: -1px;
|
|
263
|
+
height: 2px;
|
|
264
|
+
background: var(--vi-tabs-primary-color, #{tokens.$color-primary});
|
|
265
|
+
border-radius: #{func.to-rem(2px)} #{func.to-rem(2px)} 0 0;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
270
|
+
// VARIANT: card — browser-tab file-folder aesthetic
|
|
271
|
+
//
|
|
272
|
+
// Design reference: browser tabs, VS Code editor tabs
|
|
273
|
+
// Active tab: white surface merges with content area below.
|
|
274
|
+
// The cursor element becomes the white card + side borders.
|
|
275
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
276
|
+
|
|
277
|
+
.vi-tabs--card {
|
|
278
|
+
.vi-tabs__tablist {
|
|
279
|
+
background: var(--vi-tabs-surface-secondary, #{tokens.$layer-02});
|
|
280
|
+
border-bottom: 1px solid var(--vi-tabs-border-color, #{tokens.$color-border});
|
|
281
|
+
padding: var(--vi-tabs-spacing-md-md-none, 8px 8px 0);
|
|
282
|
+
gap: var(--vi-tabs-gap-sm, 4px);
|
|
283
|
+
align-items: flex-end;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// The cursor is the active card surface
|
|
287
|
+
.vi-tabs__cursor {
|
|
288
|
+
$surface-color: var(--vi-tabs-surface-primary, #{tokens.$layer-01});
|
|
289
|
+
display: block;
|
|
290
|
+
border-radius: #{func.to-rem(8px)} #{func.to-rem(8px)} 0 0;
|
|
291
|
+
background: $surface-color;
|
|
292
|
+
border: 1px solid var(--vi-tabs-border-color, #{tokens.$color-border});
|
|
293
|
+
border-bottom-color: $surface-color; // Merges with content below
|
|
294
|
+
margin-top: 8px;
|
|
295
|
+
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.vi-tabs__indicator { display: none; }
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
302
|
+
// VARIANT: enclosed — bordered box with shared header/content border
|
|
303
|
+
//
|
|
304
|
+
// Design reference: Mantine, Chakra UI enclosed tabs
|
|
305
|
+
// The entire component lives in a border box. Active tab has indicator only.
|
|
306
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
307
|
+
|
|
308
|
+
.vi-tabs--enclosed {
|
|
309
|
+
border: 1px solid var(--vi-tabs-border-color, #{tokens.$color-border});
|
|
310
|
+
border-radius: #{func.to-rem(10px)};
|
|
311
|
+
overflow: clip; // Enclosed: clip cursor inside rounded border box
|
|
312
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
|
|
313
|
+
|
|
314
|
+
.vi-tabs__tablist {
|
|
315
|
+
background: var(--vi-tabs-surface-secondary, #{tokens.$layer-02});
|
|
316
|
+
border-bottom: 1px solid var(--vi-tabs-border-color, #{tokens.$color-border});
|
|
317
|
+
padding: 0;
|
|
318
|
+
gap: 0;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Cursor: subtle active tab highlight within the enclosed strip
|
|
322
|
+
.vi-tabs__cursor {
|
|
323
|
+
display: block;
|
|
324
|
+
top: 0;
|
|
325
|
+
border-radius: 0;
|
|
326
|
+
background: rgba(#{tokens.$color-primary}, 0.06);
|
|
327
|
+
border-right: 1px solid var(--vi-tabs-border-color, #{tokens.$color-border});
|
|
328
|
+
border-left: 1px solid var(--vi-tabs-border-color, #{tokens.$color-border});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Thin indicator at bottom of strip header
|
|
332
|
+
.vi-tabs__indicator {
|
|
333
|
+
bottom: 0;
|
|
334
|
+
height: 3px;
|
|
335
|
+
background: var(--vi-tabs-primary-color, #{tokens.$color-primary});
|
|
336
|
+
box-shadow: 0 0 8px color-mix(in srgb, #{tokens.$color-primary} 50%, transparent);
|
|
337
|
+
border-radius: 0;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
::slotted(vi-tab) {
|
|
341
|
+
border-right: 1px solid var(--vi-tabs-border-color, #{tokens.$color-border}) !important;
|
|
342
|
+
min-height: 44px !important;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
347
|
+
// ORIENTATION: vertical (sidebar settings pattern)
|
|
348
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
349
|
+
|
|
350
|
+
.vi-tabs--vertical {
|
|
351
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
352
|
+
align-items: stretch;
|
|
353
|
+
|
|
354
|
+
.vi-tabs__tablist {
|
|
355
|
+
flex-direction: column;
|
|
356
|
+
align-items: stretch;
|
|
357
|
+
background: var(--vi-tabs-surface-secondary, #{tokens.$layer-02});
|
|
358
|
+
border-right: 1px solid var(--vi-tabs-border-color, #{tokens.$color-border});
|
|
359
|
+
border-bottom: none;
|
|
360
|
+
padding: var(--vi-tabs-spacing-sm-xs, 6px 4px);
|
|
361
|
+
min-width: 168px;
|
|
362
|
+
gap: var(--vi-tabs-gap-xs, 2px);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Cursor becomes the sliding bg highlight in vertical mode
|
|
366
|
+
.vi-tabs__cursor {
|
|
367
|
+
display: block;
|
|
368
|
+
left: 4px !important;
|
|
369
|
+
right: 4px;
|
|
370
|
+
width: calc(100% - 8px) !important; // Override JS width — fills sidebar
|
|
371
|
+
border-radius: #{func.to-rem(8px)};
|
|
372
|
+
background: var(--vi-tabs-surface-primary, #{tokens.$layer-01});
|
|
373
|
+
box-shadow:
|
|
374
|
+
0 1px 3px rgba(0, 0, 0, 0.08),
|
|
375
|
+
0 0 0 1px rgba(0, 0, 0, 0.04);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Vertical indicator — right edge accent line
|
|
379
|
+
.vi-tabs__indicator {
|
|
380
|
+
bottom: auto;
|
|
381
|
+
top: 0;
|
|
382
|
+
left: auto;
|
|
383
|
+
right: 0;
|
|
384
|
+
width: 3px !important;
|
|
385
|
+
height: 0;
|
|
386
|
+
border-radius: #{func.to-rem(3px)} 0 0 #{func.to-rem(3px)};
|
|
387
|
+
background: var(--vi-tabs-primary-color, #{tokens.$color-primary});
|
|
388
|
+
box-shadow: 0 0 6px color-mix(in srgb, #{tokens.$color-primary} 40%, transparent);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
::slotted(vi-tab) {
|
|
392
|
+
justify-content: flex-start !important;
|
|
393
|
+
min-height: 40px !important;
|
|
394
|
+
text-align: left !important;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
&.vi-tabs--line ::slotted(vi-tab[active]) {
|
|
398
|
+
background: var(--vi-tabs-surface-primary, #{tokens.$layer-01});
|
|
399
|
+
border-radius: #{func.to-rem(8px)};
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
404
|
+
// Reduced motion — respect system preferences
|
|
405
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
406
|
+
|
|
407
|
+
@media (prefers-reduced-motion: reduce) {
|
|
408
|
+
.vi-tabs__cursor,
|
|
409
|
+
.vi-tabs__indicator {
|
|
410
|
+
transition: opacity 100ms ease;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
415
|
+
// OVERFLOW: scroll
|
|
416
|
+
//
|
|
417
|
+
// Tablist scrolls horizontally. Fade gradients hint at hidden content.
|
|
418
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
419
|
+
|
|
420
|
+
.vi-tabs--overflow-scroll .vi-tabs__tablist {
|
|
421
|
+
overflow-x: auto;
|
|
422
|
+
overflow-y: hidden;
|
|
423
|
+
scrollbar-width: none;
|
|
424
|
+
&::-webkit-scrollbar { display: none; }
|
|
425
|
+
|
|
426
|
+
// Premium visual indicator using mask-image to fade tabs at the scroll edges
|
|
427
|
+
mask-image: linear-gradient(to right, transparent 0%, black 24px, black calc(100% - 24px), transparent 100%);
|
|
428
|
+
-webkit-mask-image: linear-gradient(to right, transparent 0%, black 24px, black calc(100% - 24px), transparent 100%);
|
|
429
|
+
|
|
430
|
+
// When at scroll start, hide left fade
|
|
431
|
+
&[data-scroll-start] {
|
|
432
|
+
mask-image: linear-gradient(to right, black 0%, black calc(100% - 24px), transparent 100%);
|
|
433
|
+
-webkit-mask-image: linear-gradient(to right, black 0%, black calc(100% - 24px), transparent 100%);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// When at scroll end, hide right fade
|
|
437
|
+
&[data-scroll-end] {
|
|
438
|
+
mask-image: linear-gradient(to right, transparent 0%, black 24px, black 100%);
|
|
439
|
+
-webkit-mask-image: linear-gradient(to right, transparent 0%, black 24px, black 100%);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// When no scroll possible, no fades
|
|
443
|
+
&[data-scroll-none] {
|
|
444
|
+
mask-image: none;
|
|
445
|
+
-webkit-mask-image: none;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
450
|
+
// OVERFLOW: wrap
|
|
451
|
+
//
|
|
452
|
+
// Tabs wrap to the next line.
|
|
453
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
454
|
+
|
|
455
|
+
.vi-tabs--overflow-wrap .vi-tabs__tablist {
|
|
456
|
+
flex-wrap: wrap;
|
|
457
|
+
overflow: visible;
|
|
458
|
+
height: auto;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
462
|
+
// OVERFLOW: menu — "More" button + dropdown
|
|
463
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
464
|
+
|
|
465
|
+
// More wrapper sits at end of tablist
|
|
466
|
+
.vi-tabs__more-wrapper {
|
|
467
|
+
position: relative;
|
|
468
|
+
display: inline-flex;
|
|
469
|
+
align-items: stretch;
|
|
470
|
+
flex-shrink: 0;
|
|
471
|
+
margin-left: auto;
|
|
472
|
+
order: 9999; // ensure it sits after visually reordered tabs
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// More trigger button
|
|
476
|
+
.vi-tabs__more-btn {
|
|
477
|
+
display: inline-flex;
|
|
478
|
+
align-items: center;
|
|
479
|
+
gap: var(--vi-tabs-gap-sm, 4px);
|
|
480
|
+
padding: var(--vi-tabs-spacing-none-lg, 0 14px);
|
|
481
|
+
min-height: 44px;
|
|
482
|
+
border: none;
|
|
483
|
+
background: transparent;
|
|
484
|
+
cursor: pointer;
|
|
485
|
+
font-family: inherit;
|
|
486
|
+
font-size: #{func.to-rem(13px)};
|
|
487
|
+
font-weight: 500;
|
|
488
|
+
color: var(--vi-tabs-text-secondary, #{tokens.$text-secondary});
|
|
489
|
+
border-radius: #{func.to-rem(6px)} #{func.to-rem(6px)} 0 0;
|
|
490
|
+
transition: color 150ms ease, background-color 150ms ease;
|
|
491
|
+
|
|
492
|
+
&:hover {
|
|
493
|
+
color: var(--vi-tabs-text-primary, #{tokens.$text-primary});
|
|
494
|
+
background: rgba(0, 0, 0, 0.04);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
&--open {
|
|
498
|
+
color: var(--vi-tabs-primary-color, #{tokens.$color-primary});
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.vi-tabs__more-chevron {
|
|
503
|
+
flex-shrink: 0;
|
|
504
|
+
transition: transform 200ms ease;
|
|
505
|
+
|
|
506
|
+
.vi-tabs__more-btn--open & {
|
|
507
|
+
transform: rotate(180deg);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// More dropdown menu
|
|
512
|
+
.vi-tabs__more-menu {
|
|
513
|
+
position: absolute;
|
|
514
|
+
top: calc(100% + 4px);
|
|
515
|
+
right: 0;
|
|
516
|
+
z-index: 100;
|
|
517
|
+
min-width: 180px;
|
|
518
|
+
background: var(--vi-tabs-surface-primary, #{tokens.$layer-01});
|
|
519
|
+
border: 1px solid var(--vi-tabs-border-color, #{tokens.$color-border});
|
|
520
|
+
border-radius: #{func.to-rem(8px)};
|
|
521
|
+
box-shadow:
|
|
522
|
+
0 4px 6px -1px rgba(0, 0, 0, 0.10),
|
|
523
|
+
0 2px 4px -1px rgba(0, 0, 0, 0.06),
|
|
524
|
+
0 0 0 1px rgba(0, 0, 0, 0.04);
|
|
525
|
+
padding: var(--vi-tabs-spacing-xs, 4px);
|
|
526
|
+
animation: vi-menu-appear 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
527
|
+
transform-origin: top right;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
@keyframes vi-menu-appear {
|
|
531
|
+
from { opacity: 0; transform: scale(0.95) translateY(-4px); }
|
|
532
|
+
to { opacity: 1; transform: scale(1) translateY(0); }
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Individual menu item
|
|
536
|
+
.vi-tabs__more-item {
|
|
537
|
+
display: flex;
|
|
538
|
+
align-items: center;
|
|
539
|
+
width: 100%;
|
|
540
|
+
padding: var(--vi-tabs-spacing-md-lg, 8px 12px);
|
|
541
|
+
border: none;
|
|
542
|
+
border-radius: #{func.to-rem(6px)};
|
|
543
|
+
background: transparent;
|
|
544
|
+
font-family: inherit;
|
|
545
|
+
font-size: #{func.to-rem(13.5px)};
|
|
546
|
+
font-weight: 500;
|
|
547
|
+
color: var(--vi-tabs-text-secondary, #{tokens.$text-secondary});
|
|
548
|
+
cursor: pointer;
|
|
549
|
+
text-align: left;
|
|
550
|
+
transition: background-color 100ms ease, color 100ms ease;
|
|
551
|
+
|
|
552
|
+
&:hover:not(:disabled) {
|
|
553
|
+
background: var(--vi-tabs-surface-secondary, #{tokens.$layer-02});
|
|
554
|
+
color: var(--vi-tabs-text-primary, #{tokens.$text-primary});
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
&:disabled {
|
|
558
|
+
color: var(--vi-tabs-text-disabled, #{tokens.$text-disabled});
|
|
559
|
+
cursor: not-allowed;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
&--active {
|
|
563
|
+
color: var(--vi-tabs-primary-color, #{tokens.$color-primary});
|
|
564
|
+
font-weight: 600;
|
|
565
|
+
background: color-mix(in srgb, #{tokens.$color-primary} 6%, transparent);
|
|
566
|
+
|
|
567
|
+
&:hover {
|
|
568
|
+
background: color-mix(in srgb, #{tokens.$color-primary} 10%, transparent);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
}
|