@fragments-sdk/ui 0.2.2 → 0.3.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.
@@ -0,0 +1,561 @@
1
+ @use '../../tokens/variables' as *;
2
+ @use '../../tokens/mixins' as *;
3
+
4
+ // ============================================
5
+ // Sidebar Root
6
+ // ============================================
7
+
8
+ .root {
9
+ position: relative;
10
+ display: flex;
11
+ flex-direction: column;
12
+ width: var(--sidebar-width);
13
+ height: 100vh;
14
+ background-color: var(--fui-bg-primary, $fui-bg-primary);
15
+ border-right: 1px solid var(--fui-border, $fui-border);
16
+ transition: width var(--fui-transition-normal, $fui-transition-normal);
17
+ overflow: hidden;
18
+ flex-shrink: 0;
19
+ }
20
+
21
+ // Right position variant
22
+ .positionRight {
23
+ border-right: none;
24
+ border-left: 1px solid var(--fui-border, $fui-border);
25
+ }
26
+
27
+ // Desktop collapsed state
28
+ .collapsed {
29
+ width: var(--sidebar-collapsed-width);
30
+
31
+ .header {
32
+ justify-content: center;
33
+ padding: var(--fui-space-3, $fui-space-3);
34
+ }
35
+
36
+ .sectionLabel {
37
+ @include visually-hidden;
38
+ }
39
+
40
+ .itemLabel,
41
+ .itemBadge,
42
+ .itemChevron {
43
+ display: none;
44
+ }
45
+
46
+ .item {
47
+ justify-content: center;
48
+ padding: var(--fui-space-3, $fui-space-3);
49
+ }
50
+
51
+ .itemIcon {
52
+ margin: 0;
53
+ }
54
+
55
+ .footer {
56
+ justify-content: center;
57
+ padding: var(--fui-space-3, $fui-space-3);
58
+ }
59
+
60
+ .collapseToggle {
61
+ margin: 0 auto;
62
+ }
63
+ }
64
+
65
+ // Mobile state
66
+ .mobile {
67
+ position: fixed;
68
+ top: 0;
69
+ left: 0;
70
+ z-index: 51;
71
+ transform: translateX(-100%);
72
+ transition: transform var(--fui-transition-normal, $fui-transition-normal);
73
+ box-shadow: var(--fui-shadow-md, $fui-shadow-md);
74
+
75
+ &.positionRight {
76
+ left: auto;
77
+ right: 0;
78
+ transform: translateX(100%);
79
+ }
80
+
81
+ &[data-state='open'] {
82
+ transform: translateX(0);
83
+ }
84
+ }
85
+
86
+ // ============================================
87
+ // Header
88
+ // ============================================
89
+
90
+ .header {
91
+ display: flex;
92
+ align-items: center;
93
+ gap: var(--fui-space-3, $fui-space-3);
94
+ padding: var(--fui-space-4, $fui-space-4) var(--fui-space-4, $fui-space-4);
95
+ border-bottom: 1px solid var(--fui-border, $fui-border);
96
+ min-height: 60px;
97
+ flex-shrink: 0;
98
+ }
99
+
100
+ // ============================================
101
+ // Navigation
102
+ // ============================================
103
+
104
+ .nav {
105
+ flex: 1;
106
+ overflow-y: auto;
107
+ overflow-x: hidden;
108
+ padding: var(--fui-space-2, $fui-space-2);
109
+ }
110
+
111
+ // ============================================
112
+ // Section
113
+ // ============================================
114
+
115
+ .section {
116
+ &:not(:first-child) {
117
+ margin-top: var(--fui-space-4, $fui-space-4);
118
+ }
119
+ }
120
+
121
+ .sectionHeader {
122
+ display: flex;
123
+ align-items: center;
124
+ justify-content: space-between;
125
+ padding: var(--fui-space-2, $fui-space-2) var(--fui-space-3, $fui-space-3);
126
+ }
127
+
128
+ .sectionLabel {
129
+ @include helper-text;
130
+
131
+ font-weight: var(--fui-font-weight-medium, $fui-font-weight-medium);
132
+ text-transform: uppercase;
133
+ letter-spacing: 0.05em;
134
+ font-size: var(--fui-font-size-2xs, $fui-font-size-2xs);
135
+ }
136
+
137
+ .sectionActionWrapper {
138
+ flex-shrink: 0;
139
+ }
140
+
141
+ .sectionAction {
142
+ @include button-reset;
143
+ @include interactive-base;
144
+
145
+ display: flex;
146
+ align-items: center;
147
+ justify-content: center;
148
+ width: 24px;
149
+ height: 24px;
150
+ border-radius: var(--fui-radius-sm, $fui-radius-sm);
151
+ color: var(--fui-text-tertiary, $fui-text-tertiary);
152
+
153
+ &:hover {
154
+ background-color: var(--fui-bg-hover, $fui-bg-hover);
155
+ color: var(--fui-text-secondary, $fui-text-secondary);
156
+ }
157
+
158
+ svg {
159
+ width: 16px;
160
+ height: 16px;
161
+ }
162
+ }
163
+
164
+ .sectionList {
165
+ list-style: none;
166
+ margin: 0;
167
+ padding: 0;
168
+ }
169
+
170
+ // ============================================
171
+ // Item
172
+ // ============================================
173
+
174
+ .itemWrapper {
175
+ list-style: none;
176
+ }
177
+
178
+ .item {
179
+ @include button-reset;
180
+ @include interactive-base;
181
+ @include text-base;
182
+
183
+ display: flex;
184
+ align-items: center;
185
+ gap: var(--fui-space-3, $fui-space-3);
186
+ width: 100%;
187
+ padding: var(--fui-space-2, $fui-space-2) var(--fui-space-3, $fui-space-3);
188
+ border-radius: var(--fui-radius-md, $fui-radius-md);
189
+ color: var(--fui-text-secondary, $fui-text-secondary);
190
+ text-decoration: none;
191
+ min-height: 40px;
192
+
193
+ &:hover:not(.itemDisabled) {
194
+ background-color: var(--fui-bg-hover, $fui-bg-hover);
195
+ color: var(--fui-text-primary, $fui-text-primary);
196
+ }
197
+
198
+ &:active:not(.itemDisabled) {
199
+ background-color: var(--fui-bg-active, $fui-bg-active);
200
+ }
201
+ }
202
+
203
+ .itemActive {
204
+ // Use accent-hover for active state to ensure WCAG AA contrast (4.5:1 minimum)
205
+ background-color: var(--fui-color-accent-hover, $fui-color-accent-hover);
206
+ color: var(--fui-text-inverse, $fui-text-inverse);
207
+
208
+ &:hover {
209
+ background-color: var(--fui-color-accent-active, $fui-color-accent-active);
210
+ color: var(--fui-text-inverse, $fui-text-inverse);
211
+ }
212
+
213
+ .itemIcon {
214
+ color: inherit;
215
+ }
216
+ }
217
+
218
+ .itemDisabled {
219
+ opacity: 0.5;
220
+ cursor: not-allowed;
221
+ }
222
+
223
+ .itemIcon {
224
+ display: flex;
225
+ align-items: center;
226
+ justify-content: center;
227
+ width: 20px;
228
+ height: 20px;
229
+ flex-shrink: 0;
230
+ color: var(--fui-text-tertiary, $fui-text-tertiary);
231
+
232
+ svg {
233
+ width: 20px;
234
+ height: 20px;
235
+ }
236
+
237
+ .item:hover:not(.itemDisabled) & {
238
+ color: var(--fui-text-secondary, $fui-text-secondary);
239
+ }
240
+ }
241
+
242
+ .itemLabel {
243
+ flex: 1;
244
+ text-align: left;
245
+ white-space: nowrap;
246
+ overflow: hidden;
247
+ text-overflow: ellipsis;
248
+ }
249
+
250
+ .itemBadge {
251
+ display: flex;
252
+ align-items: center;
253
+ justify-content: center;
254
+ padding: var(--fui-space-1, $fui-space-1) var(--fui-space-2, $fui-space-2);
255
+ font-size: var(--fui-font-size-2xs, $fui-font-size-2xs);
256
+ font-weight: var(--fui-font-weight-medium, $fui-font-weight-medium);
257
+ // Use accent-hover for WCAG AA contrast (4.5:1 minimum)
258
+ background-color: var(--fui-color-accent-hover, $fui-color-accent-hover);
259
+ color: var(--fui-text-inverse, $fui-text-inverse);
260
+ border-radius: var(--fui-radius-full, $fui-radius-full);
261
+ min-width: 20px;
262
+ line-height: 1;
263
+
264
+ .itemActive & {
265
+ background-color: var(--fui-bg-primary, $fui-bg-primary);
266
+ color: var(--fui-color-accent, $fui-color-accent);
267
+ }
268
+ }
269
+
270
+ .itemChevron {
271
+ display: flex;
272
+ align-items: center;
273
+ justify-content: center;
274
+ width: 16px;
275
+ height: 16px;
276
+ flex-shrink: 0;
277
+ transition: transform var(--fui-transition-fast, $fui-transition-fast);
278
+
279
+ svg {
280
+ width: 16px;
281
+ height: 16px;
282
+ }
283
+ }
284
+
285
+ .itemExpanded .itemChevron {
286
+ transform: rotate(90deg);
287
+ }
288
+
289
+ // ============================================
290
+ // Submenu
291
+ // ============================================
292
+
293
+ .submenu {
294
+ list-style: none;
295
+ margin: 0;
296
+ padding: 0;
297
+ padding-left: var(--fui-space-6, $fui-space-6);
298
+ overflow: hidden;
299
+ max-height: 0;
300
+ transition: max-height var(--fui-transition-normal, $fui-transition-normal);
301
+ }
302
+
303
+ // Submenu wrapper (for proper ARIA list structure)
304
+ .submenuWrapper {
305
+ list-style: none;
306
+ }
307
+
308
+ .itemExpanded + .submenuWrapper .submenu {
309
+ max-height: 500px; // Generous max for animation
310
+ }
311
+
312
+ // ============================================
313
+ // Sub Item
314
+ // ============================================
315
+
316
+ .subItemWrapper {
317
+ list-style: none;
318
+ }
319
+
320
+ .subItem {
321
+ @include button-reset;
322
+ @include interactive-base;
323
+ @include text-base;
324
+
325
+ display: flex;
326
+ align-items: center;
327
+ width: 100%;
328
+ padding: var(--fui-space-2, $fui-space-2) var(--fui-space-3, $fui-space-3);
329
+ border-radius: var(--fui-radius-md, $fui-radius-md);
330
+ color: var(--fui-text-secondary, $fui-text-secondary);
331
+ text-decoration: none;
332
+ font-size: var(--fui-font-size-sm, $fui-font-size-sm);
333
+ min-height: 36px;
334
+ position: relative;
335
+
336
+ // Left border indicator
337
+ &::before {
338
+ content: '';
339
+ position: absolute;
340
+ left: 0;
341
+ top: 50%;
342
+ transform: translateY(-50%);
343
+ width: 2px;
344
+ height: 16px;
345
+ background-color: transparent;
346
+ border-radius: var(--fui-radius-full, $fui-radius-full);
347
+ transition: background-color var(--fui-transition-fast, $fui-transition-fast);
348
+ }
349
+
350
+ &:hover:not(.subItemDisabled) {
351
+ background-color: var(--fui-bg-hover, $fui-bg-hover);
352
+ color: var(--fui-text-primary, $fui-text-primary);
353
+ }
354
+
355
+ &:active:not(.subItemDisabled) {
356
+ background-color: var(--fui-bg-active, $fui-bg-active);
357
+ }
358
+ }
359
+
360
+ .subItemActive {
361
+ // Use accent-hover for WCAG AA contrast (4.5:1 minimum)
362
+ color: var(--fui-color-accent-hover, $fui-color-accent-hover);
363
+ font-weight: var(--fui-font-weight-medium, $fui-font-weight-medium);
364
+
365
+ &::before {
366
+ background-color: var(--fui-color-accent-hover, $fui-color-accent-hover);
367
+ }
368
+ }
369
+
370
+ .subItemDisabled {
371
+ opacity: 0.5;
372
+ cursor: not-allowed;
373
+ }
374
+
375
+ // ============================================
376
+ // Footer
377
+ // ============================================
378
+
379
+ .footer {
380
+ display: flex;
381
+ flex-direction: column;
382
+ gap: var(--fui-space-2, $fui-space-2);
383
+ padding: var(--fui-space-4, $fui-space-4);
384
+ border-top: 1px solid var(--fui-border, $fui-border);
385
+ margin-top: auto;
386
+ flex-shrink: 0;
387
+ }
388
+
389
+ // ============================================
390
+ // Trigger (Mobile hamburger)
391
+ // ============================================
392
+
393
+ .trigger {
394
+ @include button-reset;
395
+ @include interactive-base;
396
+
397
+ display: flex;
398
+ align-items: center;
399
+ justify-content: center;
400
+ width: 44px;
401
+ height: 44px;
402
+ border-radius: var(--fui-radius-md, $fui-radius-md);
403
+ color: var(--fui-text-primary, $fui-text-primary);
404
+
405
+ &:hover {
406
+ background-color: var(--fui-bg-hover, $fui-bg-hover);
407
+ }
408
+
409
+ svg {
410
+ width: 24px;
411
+ height: 24px;
412
+ }
413
+ }
414
+
415
+ // ============================================
416
+ // Overlay (Mobile backdrop)
417
+ // ============================================
418
+
419
+ .overlay {
420
+ position: fixed;
421
+ inset: 0;
422
+ z-index: 50;
423
+ background-color: var(--fui-backdrop, $fui-backdrop);
424
+ opacity: 0;
425
+ transition: opacity var(--fui-transition-normal, $fui-transition-normal);
426
+
427
+ &[data-state='open'] {
428
+ opacity: 1;
429
+ }
430
+ }
431
+
432
+ // ============================================
433
+ // Collapse Toggle
434
+ // ============================================
435
+
436
+ .collapseToggle {
437
+ @include button-reset;
438
+ @include interactive-base;
439
+
440
+ display: flex;
441
+ align-items: center;
442
+ justify-content: center;
443
+ width: 32px;
444
+ height: 32px;
445
+ border-radius: var(--fui-radius-md, $fui-radius-md);
446
+ color: var(--fui-text-secondary, $fui-text-secondary);
447
+ margin-left: auto;
448
+
449
+ &:hover {
450
+ background-color: var(--fui-bg-hover, $fui-bg-hover);
451
+ color: var(--fui-text-primary, $fui-text-primary);
452
+ }
453
+
454
+ svg {
455
+ width: 20px;
456
+ height: 20px;
457
+ }
458
+ }
459
+
460
+ // ============================================
461
+ // Offcanvas Mode (slides completely off-screen)
462
+ // ============================================
463
+
464
+ .offcanvas {
465
+ transform: translateX(-100%);
466
+ position: absolute;
467
+
468
+ &.positionRight {
469
+ transform: translateX(100%);
470
+ }
471
+ }
472
+
473
+ // ============================================
474
+ // Rail (toggle to expand/collapse sidebar)
475
+ // ============================================
476
+
477
+ .rail {
478
+ @include button-reset;
479
+
480
+ position: absolute;
481
+ top: 0;
482
+ right: 0;
483
+ width: 16px;
484
+ height: 100%;
485
+ cursor: pointer;
486
+ z-index: 10;
487
+ display: flex;
488
+ align-items: center;
489
+ justify-content: center;
490
+ background: transparent;
491
+ border: none;
492
+ padding: 0;
493
+
494
+ .positionRight & {
495
+ right: auto;
496
+ left: 0;
497
+ }
498
+
499
+ // Visible rail indicator
500
+ &::before {
501
+ content: '';
502
+ position: absolute;
503
+ top: 50%;
504
+ transform: translateY(-50%);
505
+ right: 4px;
506
+ width: 4px;
507
+ height: 40px;
508
+ border-radius: var(--fui-radius-full, $fui-radius-full);
509
+ background-color: var(--fui-border, $fui-border);
510
+ opacity: 0;
511
+ transition:
512
+ opacity var(--fui-transition-fast, $fui-transition-fast),
513
+ background-color var(--fui-transition-fast, $fui-transition-fast),
514
+ height var(--fui-transition-fast, $fui-transition-fast);
515
+
516
+ .positionRight & {
517
+ right: auto;
518
+ left: 4px;
519
+ }
520
+ }
521
+
522
+ // Show indicator on hover
523
+ &:hover::before {
524
+ opacity: 1;
525
+ height: 56px;
526
+ background-color: var(--fui-color-accent, $fui-color-accent);
527
+ }
528
+
529
+ &:focus-visible::before {
530
+ opacity: 1;
531
+ background-color: var(--fui-color-accent, $fui-color-accent);
532
+ }
533
+ }
534
+
535
+ // When collapsed, always show the rail indicator
536
+ .railCollapsed::before {
537
+ opacity: 0.7;
538
+ }
539
+
540
+ // ============================================
541
+ // Menu Skeleton (loading state)
542
+ // ============================================
543
+
544
+ .menuSkeleton {
545
+ display: flex;
546
+ flex-direction: column;
547
+ gap: var(--fui-space-2, $fui-space-2);
548
+ padding: var(--fui-space-2, $fui-space-2);
549
+ }
550
+
551
+ .skeletonItem {
552
+ display: flex;
553
+ align-items: center;
554
+ gap: var(--fui-space-3, $fui-space-3);
555
+ padding: var(--fui-space-2, $fui-space-2) var(--fui-space-3, $fui-space-3);
556
+ min-height: 40px;
557
+ }
558
+
559
+ .skeletonLabel {
560
+ flex: 1;
561
+ }