@fastwork/xosmoz-svelte 0.0.21

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,900 @@
1
+ <script lang="ts">
2
+ import { onDestroy } from 'svelte'
3
+ import { lightTheme, darkTheme } from '@fastwork/xosmoz-theme'
4
+ import type { ColorToken } from '@fastwork/xosmoz-theme'
5
+ import {
6
+ detectDarkTheme,
7
+ watchThemeChanges,
8
+ } from '../../utils/themeDetection'
9
+ import { calculateAPCA, toHex } from '../../utils/colorCalculations'
10
+ import { createClipboardHandler } from '../../utils/clipboard'
11
+
12
+ let isDarkTheme = $state(false)
13
+ let theme = $derived.by(() => (isDarkTheme ? darkTheme : lightTheme))
14
+ let colors = $derived.by(() => theme.colors)
15
+
16
+ let cleanupThemeWatcher: (() => void) | null = null
17
+
18
+ // Update colors based on theme
19
+ function updateColors() {
20
+ isDarkTheme = detectDarkTheme()
21
+ colors = isDarkTheme ? darkTheme.colors : lightTheme.colors
22
+ }
23
+
24
+ // Initial theme detection and watch for changes
25
+ if (typeof window !== 'undefined') {
26
+ updateColors()
27
+ cleanupThemeWatcher = watchThemeChanges(updateColors)
28
+ }
29
+
30
+ // Cleanup observer on component destroy
31
+ onDestroy(() => {
32
+ if (cleanupThemeWatcher) {
33
+ cleanupThemeWatcher()
34
+ }
35
+ })
36
+
37
+ // Define color groups with their scales
38
+ const colorGroups = $derived.by(() => [
39
+ {
40
+ title: 'Brand Colors',
41
+ description:
42
+ 'Fastwork brand color palette used throughout the design system.',
43
+ colors: [
44
+ {
45
+ name: 'Fastwork',
46
+ scale: colors.fastwork,
47
+ key: 'fastwork',
48
+ },
49
+ ],
50
+ },
51
+ {
52
+ title: 'Neutral Colors',
53
+ description:
54
+ 'Gray scale for backgrounds, surfaces, text, and borders.',
55
+ colors: [{ name: 'Gray', scale: colors.gray, key: 'gray' }],
56
+ },
57
+ {
58
+ title: 'Semantic Colors',
59
+ description: 'Color palettes for UI states and feedback messages.',
60
+ colors: [
61
+ {
62
+ name: 'Green',
63
+ scale: colors.green,
64
+ key: 'green',
65
+ usage: 'Success states',
66
+ },
67
+ {
68
+ name: 'Red',
69
+ scale: colors.red,
70
+ key: 'red',
71
+ usage: 'Error states',
72
+ },
73
+ {
74
+ name: 'Amber',
75
+ scale: colors.amber,
76
+ key: 'amber',
77
+ usage: 'Warning states',
78
+ },
79
+ {
80
+ name: 'Cyan',
81
+ scale: colors.cyan,
82
+ key: 'cyan',
83
+ usage: 'Info states',
84
+ },
85
+ ],
86
+ },
87
+ {
88
+ title: 'Extended Palette',
89
+ description:
90
+ 'Additional colors for charts, data visualization, and extended UI needs.',
91
+ colors: [
92
+ {
93
+ name: 'Mint',
94
+ scale: colors.mint,
95
+ key: 'mint',
96
+ },
97
+ {
98
+ name: 'Orange',
99
+ scale: colors.orange,
100
+ key: 'orange',
101
+ },
102
+ { name: 'Purple', scale: colors.purple, key: 'purple' },
103
+ ],
104
+ },
105
+ {
106
+ title: 'Alpha Colors',
107
+ description:
108
+ 'Transparent overlay colors for layering and depth effects.',
109
+ colors: [
110
+ {
111
+ name: 'Black Alpha',
112
+ scale: colors.blackAlpha,
113
+ key: 'blackAlpha',
114
+ },
115
+ {
116
+ name: 'White Alpha',
117
+ scale: colors.whiteAlpha,
118
+ key: 'whiteAlpha',
119
+ },
120
+ ],
121
+ },
122
+ ])
123
+
124
+ // Track copied state
125
+ let copiedVar = $state('')
126
+
127
+ // Create clipboard handler with state management
128
+ const copyToClipboard = createClipboardHandler(
129
+ () => copiedVar,
130
+ (val) => (copiedVar = val),
131
+ )
132
+
133
+ // Generate CSS variable name
134
+ function getCSSVarName(key: string, value: string): string {
135
+ // Convert camelCase to kebab-case
136
+ const kebabKey = key.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
137
+ return `--xz-color-${kebabKey}-${value}`
138
+ }
139
+
140
+ // Get all scale values from a color token
141
+ function getScaleValues(
142
+ scale: ColorToken,
143
+ ): { value: string; color: string }[] {
144
+ const values: { value: string; color: string }[] = []
145
+ const keys: (keyof ColorToken)[] = [
146
+ 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000,
147
+ ]
148
+
149
+ for (const key of keys) {
150
+ if (scale[key]) {
151
+ values.push({ value: String(key), color: scale[key] })
152
+ }
153
+ }
154
+
155
+ return values
156
+ }
157
+
158
+ // Get foreground color for preview based on scale value
159
+ function getForegroundColor(scale: ColorToken, value: string): string {
160
+ // For colors 100, 200, 300: use 900
161
+ if (['100', '200', '300'].includes(value)) {
162
+ return scale[900] || '#000000'
163
+ }
164
+ // For colors 700, 800: use white or very light color for low contrast demo
165
+ if (['700', '800'].includes(value)) {
166
+ return '#ffffff'
167
+ }
168
+ // Default: transparent (will be invisible)
169
+ return 'transparent'
170
+ }
171
+ </script>
172
+
173
+ <div class="color-page">
174
+ <div class="header">
175
+ <h1>Color Tokens</h1>
176
+ <p class="lead">
177
+ Foundational color palettes that form the building blocks of the
178
+ Xosmoz design system. All colors use the OKLCH color format for
179
+ perceptually uniform lightness and are used to construct light and
180
+ dark themes.
181
+ </p>
182
+ <div class="theme-indicator">
183
+ <span class="indicator-label">Current Theme:</span>
184
+ <span class="indicator-badge" class:dark={isDarkTheme}>
185
+ {isDarkTheme ? '🌙 Dark' : '☀️ Light'}
186
+ </span>
187
+ <span class="indicator-hint">
188
+ Switch using the theme icon in the toolbar
189
+ </span>
190
+ </div>
191
+ </div>
192
+
193
+ <!-- Color Scale Reference Guide -->
194
+ <section class="scale-reference">
195
+ <h2>Color Usage Guidelines</h2>
196
+ <p class="scale-intro">
197
+ Each color palette follows a systematic 10-step scale (100-1000)
198
+ designed for specific UI purposes. Use this guide to select the
199
+ appropriate color for your design needs.
200
+ <br />
201
+ <br />
202
+ <strong>Note:</strong> These are flexible guidelines to help you choose
203
+ from the foundation colors, not strict rules that must be followed.
204
+ </p>
205
+
206
+ <div class="scale-guide">
207
+ <div class="scale-item">
208
+ <div class="scale-range">
209
+ <div class="range-header">
210
+ <span class="range-badge bg">100-300</span>
211
+ <div class="range-circles">
212
+ <div
213
+ class="range-circle"
214
+ style="background-color: {colors
215
+ .fastwork[100]};"
216
+ title="100"
217
+ ></div>
218
+ <div
219
+ class="range-circle"
220
+ style="background-color: {colors
221
+ .fastwork[200]};"
222
+ title="200"
223
+ ></div>
224
+ <div
225
+ class="range-circle"
226
+ style="background-color: {colors
227
+ .fastwork[300]};"
228
+ title="300"
229
+ ></div>
230
+ </div>
231
+ </div>
232
+ <span class="range-label"
233
+ >Soft colors, component backgrounds</span
234
+ >
235
+ </div>
236
+ <p class="scale-description">
237
+ Subtle to medium background colors for cards, panels, and
238
+ component surfaces. Use 100 for the lightest backgrounds,
239
+ 200 for secondary surfaces, and 300 for more prominent
240
+ backgrounds.
241
+ </p>
242
+ </div>
243
+
244
+ <div class="scale-item">
245
+ <div class="scale-range">
246
+ <div class="range-header">
247
+ <span class="range-badge border">400-600</span>
248
+ <div class="range-circles">
249
+ <div
250
+ class="range-circle"
251
+ style="background-color: {colors
252
+ .fastwork[400]};"
253
+ title="400"
254
+ ></div>
255
+ <div
256
+ class="range-circle"
257
+ style="background-color: {colors
258
+ .fastwork[500]};"
259
+ title="500"
260
+ ></div>
261
+ <div
262
+ class="range-circle"
263
+ style="background-color: {colors
264
+ .fastwork[600]};"
265
+ title="600"
266
+ ></div>
267
+ </div>
268
+ </div>
269
+ <span class="range-label"
270
+ >Line, Borders, Other Utilities</span
271
+ >
272
+ </div>
273
+ <p class="scale-description">
274
+ Line colors for UI elements. 400 for subtle borders, 500 for
275
+ standard borders, and 600 for emphasized borders or other
276
+ utilities.
277
+ </p>
278
+ </div>
279
+
280
+ <div class="scale-item">
281
+ <div class="scale-range">
282
+ <div class="range-header">
283
+ <span class="range-badge solid">700-800</span>
284
+ <div class="range-circles">
285
+ <div
286
+ class="range-circle"
287
+ style="background-color: {colors
288
+ .fastwork[700]};"
289
+ title="700"
290
+ ></div>
291
+ <div
292
+ class="range-circle"
293
+ style="background-color: {colors
294
+ .fastwork[800]};"
295
+ title="800"
296
+ ></div>
297
+ </div>
298
+ </div>
299
+ <span class="range-label">Background, Solid Fills</span>
300
+ </div>
301
+ <p class="scale-description">
302
+ Background, solid fill colors for buttons, badges, and
303
+ interactive elements. 700 for normal state, 800 for hover
304
+ states and feedback.
305
+ </p>
306
+ </div>
307
+
308
+ <div class="scale-item">
309
+ <div class="scale-range">
310
+ <div class="range-header">
311
+ <span class="range-badge text">900-1000</span>
312
+ <div class="range-circles">
313
+ <div
314
+ class="range-circle"
315
+ style="background-color: {colors
316
+ .fastwork[900]};"
317
+ title="900"
318
+ ></div>
319
+ <div
320
+ class="range-circle"
321
+ style="background-color: {colors
322
+ .fastwork[1000]};"
323
+ title="1000"
324
+ ></div>
325
+ </div>
326
+ </div>
327
+ <span class="range-label">Content, Text & Icons</span>
328
+ </div>
329
+ <p class="scale-description">
330
+ High contrast colors for text and icons. 900 for primary
331
+ text and 1000 for maximum contrast text.
332
+ </p>
333
+ </div>
334
+ </div>
335
+ </section>
336
+
337
+ <!-- Foundational Color Scales -->
338
+ <section class="section">
339
+ {#each colorGroups as group, idx (idx)}
340
+ <div class="color-group">
341
+ <div class="group-header">
342
+ <h3>{group.title}</h3>
343
+ <p>{group.description}</p>
344
+ </div>
345
+
346
+ {#each group.colors as colorItem, idx (idx)}
347
+ <div class="color-scale">
348
+ <div class="scale-header">
349
+ <h4>{colorItem.name}</h4>
350
+ </div>
351
+
352
+ <div class="color-table">
353
+ <div class="table-header">
354
+ <div class="col-preview">Preview</div>
355
+ <div class="col-name">Name</div>
356
+ <div class="col-token">Token</div>
357
+ <div class="col-value">OKLCH</div>
358
+ <div class="col-hex">Hex</div>
359
+ </div>
360
+
361
+ {#each getScaleValues(colorItem.scale) as { value, color }, idx (idx)}
362
+ {@const cssVar = getCSSVarName(
363
+ colorItem.key,
364
+ value,
365
+ )}
366
+ {@const tokenName = `${colorItem.key}.${value}`}
367
+ {@const hexColor = toHex(color)}
368
+ {@const isAlpha =
369
+ colorItem.key === 'blackAlpha' ||
370
+ colorItem.key === 'whiteAlpha'}
371
+ {@const fgColor = isAlpha
372
+ ? colorItem.key === 'blackAlpha'
373
+ ? '#ffffff'
374
+ : '#000000'
375
+ : getForegroundColor(
376
+ colorItem.scale,
377
+ value,
378
+ )}
379
+ {@const labelColor = fgColor}
380
+ {@const showContrast =
381
+ !isAlpha &&
382
+ [
383
+ '100',
384
+ '200',
385
+ '300',
386
+ '700',
387
+ '800',
388
+ ].includes(value)}
389
+ {@const contrastRatio = showContrast
390
+ ? calculateAPCA(fgColor, color)
391
+ : 0}
392
+ <div class="table-row">
393
+ <div class="col-preview">
394
+ <div
395
+ class="color-swatch"
396
+ class:has-alpha={isAlpha}
397
+ style={isAlpha
398
+ ? `--alpha-color: ${color};`
399
+ : `background-color: ${color};`}
400
+ title={color}
401
+ >
402
+ <div
403
+ class="swatch-label"
404
+ class:show-contrast={showContrast}
405
+ style="color: {labelColor};"
406
+ >
407
+ A
408
+ {#if showContrast}
409
+ <span class="contrast-ratio"
410
+ >{contrastRatio}</span
411
+ >
412
+ {/if}
413
+ </div>
414
+ </div>
415
+ </div>
416
+ <div class="col-name">
417
+ {tokenName}
418
+ </div>
419
+ <div class="col-token">
420
+ <button
421
+ class="token-button"
422
+ class:copied={copiedVar === cssVar}
423
+ onclick={() =>
424
+ copyToClipboard(cssVar)}
425
+ title="Click to copy CSS variable"
426
+ >
427
+ {cssVar}
428
+ </button>
429
+ </div>
430
+ <div class="col-value">
431
+ <button
432
+ class="value-button"
433
+ class:copied={copiedVar === color}
434
+ onclick={() =>
435
+ copyToClipboard(color)}
436
+ title="Click to copy OKLCH"
437
+ >
438
+ {color}
439
+ </button>
440
+ </div>
441
+ <div class="col-hex">
442
+ {#if hexColor.includes('Out of gamut')}
443
+ <span
444
+ class="out-of-gamut"
445
+ title="This color is outside the sRGB gamut. OKLCH can represent more vibrant colors than hex/sRGB."
446
+ >
447
+ {hexColor}
448
+ </span>
449
+ {:else}
450
+ <button
451
+ class="value-button"
452
+ class:copied={copiedVar ===
453
+ hexColor}
454
+ onclick={() =>
455
+ copyToClipboard(hexColor)}
456
+ title="Click to copy Hex"
457
+ >
458
+ {hexColor}
459
+ </button>
460
+ {/if}
461
+ </div>
462
+ </div>
463
+ {/each}
464
+ </div>
465
+ </div>
466
+ {/each}
467
+ </div>
468
+ {/each}
469
+ </section>
470
+ </div>
471
+
472
+ <style>
473
+ .color-page {
474
+ overflow: hidden;
475
+ background-color: var(--xz-color-bg-100);
476
+ font-family: var(--xz-font-family-primary, system-ui);
477
+ padding: 2rem 0;
478
+ max-width: 100%;
479
+ padding: 2rem;
480
+ }
481
+
482
+ .header {
483
+ margin-bottom: 3rem;
484
+ }
485
+
486
+ h1 {
487
+ font-size: 2.5rem;
488
+ font-weight: 700;
489
+ margin-bottom: 0.75rem;
490
+ color: var(--xz-color-text);
491
+ }
492
+
493
+ .lead {
494
+ font-size: 1.125rem;
495
+ color: var(--xz-color-content-200);
496
+ line-height: 1.6;
497
+ max-width: 800px;
498
+ }
499
+
500
+ .theme-indicator {
501
+ display: flex;
502
+ align-items: center;
503
+ gap: 0.75rem;
504
+ margin-top: 1.5rem;
505
+ padding: 1rem;
506
+ background: var(--xz-color-bg-200);
507
+ border-radius: var(--xz-radius-md, 0.375rem);
508
+ border: 1px solid var(--xz-color-line-100);
509
+ max-width: fit-content;
510
+ }
511
+
512
+ .indicator-label {
513
+ font-size: 0.875rem;
514
+ font-weight: 600;
515
+ color: var(--xz-color-content-100);
516
+ }
517
+
518
+ .indicator-badge {
519
+ padding: 0.375rem 0.75rem;
520
+ border-radius: var(--xz-radius-sm, 0.25rem);
521
+ font-size: 0.875rem;
522
+ font-weight: 600;
523
+ background: var(--xz-color-primary-soft-200);
524
+ color: var(--xz-color-primary-content-100);
525
+ transition: all 0.2s;
526
+ }
527
+
528
+ .indicator-badge.dark {
529
+ background: var(--xz-color-primary-soft-300);
530
+ color: var(--xz-color-primary-content-200);
531
+ }
532
+
533
+ .indicator-hint {
534
+ font-size: 0.8125rem;
535
+ color: var(--xz-color-content-200);
536
+ font-style: italic;
537
+ }
538
+
539
+ /* Scale Reference Guide Styles */
540
+ .scale-reference {
541
+ margin-bottom: 4rem;
542
+ padding: 2rem;
543
+ background: var(--xz-color-bg-200, #fafafa);
544
+ border-radius: var(--xz-radius-lg, 0.5rem);
545
+ }
546
+
547
+ .scale-reference h2 {
548
+ font-size: 1.75rem;
549
+ font-weight: 600;
550
+ color: var(--xz-color-text);
551
+ margin-bottom: 0.5rem;
552
+ }
553
+
554
+ .scale-intro {
555
+ font-size: 1rem;
556
+ color: var(--xz-color-content-200);
557
+ line-height: 1.6;
558
+ margin-bottom: 2rem;
559
+ }
560
+
561
+ .scale-guide {
562
+ display: grid;
563
+ gap: 1.5rem;
564
+ }
565
+
566
+ .scale-item {
567
+ padding: 1.25rem;
568
+ background: var(--xz-color-bg-100, #ffffff);
569
+ border-radius: var(--xz-radius-md, 0.375rem);
570
+ border: 1px solid var(--xz-color-line-100);
571
+ }
572
+
573
+ .scale-range {
574
+ display: flex;
575
+ gap: 0.75rem;
576
+ margin-bottom: 0.5rem;
577
+ }
578
+
579
+ .range-header {
580
+ display: flex;
581
+ flex-direction: column;
582
+ gap: 0.5rem;
583
+ align-items: flex-start;
584
+ }
585
+
586
+ .range-circles {
587
+ display: flex;
588
+ gap: 0.375rem;
589
+ padding-left: 0.25rem;
590
+ }
591
+
592
+ .range-circle {
593
+ width: 16px;
594
+ height: 16px;
595
+ border-radius: 50%;
596
+ transition: transform 0.15s;
597
+ cursor: pointer;
598
+ }
599
+
600
+ .range-circle:hover {
601
+ transform: scale(1.15);
602
+ }
603
+
604
+ .range-badge {
605
+ display: inline-flex;
606
+ align-items: center;
607
+ justify-content: center;
608
+ padding: 0.25rem 0.75rem;
609
+ border-radius: var(--xz-radius-sm, 0.25rem);
610
+ font-weight: 600;
611
+ font-size: 0.875rem;
612
+ font-family: var(--xz-font-family-mono, monospace);
613
+ min-width: 80px;
614
+ }
615
+
616
+ .range-badge.bg {
617
+ background: var(--xz-color-fastwork-200);
618
+ color: var(--xz-color-fastwork-900);
619
+ }
620
+
621
+ .range-badge.border {
622
+ background: var(--xz-color-fastwork-400);
623
+ color: var(--xz-color-fastwork-1000);
624
+ }
625
+
626
+ .range-badge.solid {
627
+ background: var(--xz-color-fastwork-700);
628
+ color: #ffffff;
629
+ }
630
+
631
+ .range-badge.text {
632
+ background: var(--xz-color-fastwork-800);
633
+ color: #ffffff;
634
+ }
635
+
636
+ .range-label {
637
+ font-weight: 600;
638
+ font-size: 1rem;
639
+ color: var(--xz-color-text);
640
+ }
641
+
642
+ .scale-description {
643
+ font-size: 0.9375rem;
644
+ color: var(--xz-color-content-200);
645
+ line-height: 1.6;
646
+ margin: 0;
647
+ }
648
+
649
+ .section {
650
+ margin-bottom: 4rem;
651
+ }
652
+
653
+ .color-group {
654
+ margin-bottom: 3rem;
655
+ }
656
+
657
+ .group-header {
658
+ margin-bottom: 1.5rem;
659
+ }
660
+
661
+ .group-header h3 {
662
+ font-size: 1.5rem;
663
+ font-weight: 600;
664
+ color: var(--xz-color-text);
665
+ margin-bottom: 0.25rem;
666
+ }
667
+
668
+ .group-header p {
669
+ font-size: 0.9375rem;
670
+ color: var(--xz-color-content-200);
671
+ margin: 0;
672
+ }
673
+
674
+ .color-scale {
675
+ margin-bottom: 2rem;
676
+ }
677
+
678
+ .scale-header {
679
+ display: flex;
680
+ align-items: center;
681
+ gap: 0.75rem;
682
+ margin-bottom: 1rem;
683
+ }
684
+
685
+ .scale-header h4 {
686
+ font-size: 1.125rem;
687
+ font-weight: 600;
688
+ color: var(--xz-color-text);
689
+ margin: 0;
690
+ }
691
+
692
+ .color-table {
693
+ margin-top: 1rem;
694
+ }
695
+
696
+ .table-header {
697
+ display: grid;
698
+ grid-template-columns: 80px 1fr 2fr 1.5fr 1fr;
699
+ gap: 1rem;
700
+ padding: 0.75rem 1rem;
701
+ background: var(--xz-color-bg-300, #f5f5f5);
702
+ border-radius: var(--xz-radius-md, 0.375rem);
703
+ font-weight: 600;
704
+ font-size: 0.875rem;
705
+ color: var(--xz-color-content-100, #333);
706
+ margin-bottom: 0.5rem;
707
+ }
708
+
709
+ .table-row {
710
+ display: grid;
711
+ grid-template-columns: 80px 1fr 2fr 1.5fr 1fr;
712
+ gap: 1rem;
713
+ padding: 0.75rem 1rem;
714
+ align-items: center;
715
+ border-bottom: 1px solid var(--xz-color-border, rgba(0, 0, 0, 0.08));
716
+ transition: background-color 0.15s;
717
+ }
718
+
719
+ .table-row:hover {
720
+ background: var(--xz-color-bg-200, #fafafa);
721
+ }
722
+
723
+ .table-row:last-child {
724
+ border-bottom: none;
725
+ }
726
+
727
+ .color-swatch {
728
+ width: 48px;
729
+ height: 48px;
730
+ border-radius: var(--xz-radius-md, 0.375rem);
731
+ border: 1px solid rgba(0, 0, 0, 0.1);
732
+ cursor: pointer;
733
+ transition: transform 0.15s;
734
+ position: relative;
735
+ display: flex;
736
+ align-items: center;
737
+ justify-content: center;
738
+ }
739
+
740
+ .color-swatch.has-alpha {
741
+ background-image:
742
+ linear-gradient(45deg, #e0e0e0 25%, transparent 25%),
743
+ linear-gradient(-45deg, #e0e0e0 25%, transparent 25%),
744
+ linear-gradient(45deg, transparent 75%, #e0e0e0 75%),
745
+ linear-gradient(-45deg, transparent 75%, #e0e0e0 75%);
746
+ background-size: 8px 8px;
747
+ background-position:
748
+ 0 0,
749
+ 0 4px,
750
+ 4px -4px,
751
+ -4px 0px;
752
+ background-color: #f5f5f5;
753
+ }
754
+
755
+ .color-swatch.has-alpha::before {
756
+ content: '';
757
+ position: absolute;
758
+ inset: 0;
759
+ background-color: var(--alpha-color);
760
+ border-radius: inherit;
761
+ }
762
+
763
+ .color-swatch:hover {
764
+ transform: scale(1.05);
765
+ }
766
+
767
+ .swatch-label {
768
+ font-size: 1.25rem;
769
+ font-weight: 700;
770
+ opacity: 1;
771
+ user-select: none;
772
+ position: relative;
773
+ z-index: 1;
774
+ display: flex;
775
+ flex-direction: column;
776
+ align-items: center;
777
+ gap: 0.125rem;
778
+ }
779
+
780
+ .swatch-label.show-contrast {
781
+ opacity: 1;
782
+ }
783
+
784
+ .contrast-ratio {
785
+ font-size: 0.5rem;
786
+ font-weight: 500;
787
+ opacity: 0.7;
788
+ line-height: 1;
789
+ }
790
+
791
+ .token-button,
792
+ .value-button {
793
+ background: none;
794
+ border: none;
795
+ padding: 0.375rem 0.5rem;
796
+ font-family: var(--xz-font-family-mono, monospace);
797
+ font-size: 0.875rem;
798
+ color: var(--xz-color-primary-content-100);
799
+ cursor: pointer;
800
+ border-radius: var(--xz-radius-sm, 0.25rem);
801
+ transition: all 0.15s;
802
+ text-align: left;
803
+ }
804
+
805
+ .token-button:hover,
806
+ .value-button:hover {
807
+ background: var(--xz-color-bg-300, #f0f0f0);
808
+ }
809
+
810
+ .token-button.copied,
811
+ .value-button.copied {
812
+ background: var(--xz-color-success-bg-100, #22c55e);
813
+ color: white;
814
+ }
815
+
816
+ .out-of-gamut {
817
+ font-family: var(--xz-font-family-mono, monospace);
818
+ font-size: 0.875rem;
819
+ color: var(--xz-color-content-200, #666);
820
+ font-style: italic;
821
+ cursor: help;
822
+ }
823
+
824
+ .col-name,
825
+ .col-token,
826
+ .col-value {
827
+ font-family: var(--xz-font-family-mono, monospace);
828
+ }
829
+
830
+ /* Responsive */
831
+ @media (max-width: 768px) {
832
+ .scale-reference {
833
+ padding: 1.5rem;
834
+ }
835
+
836
+ .scale-reference h2 {
837
+ font-size: 1.5rem;
838
+ }
839
+
840
+ .scale-range {
841
+ flex-direction: column;
842
+ align-items: flex-start;
843
+ gap: 0.5rem;
844
+ }
845
+
846
+ .range-header {
847
+ gap: 0.375rem;
848
+ }
849
+
850
+ .range-circles {
851
+ gap: 0.25rem;
852
+ }
853
+
854
+ .range-circle {
855
+ width: 20px;
856
+ height: 20px;
857
+ }
858
+
859
+ .range-badge {
860
+ font-size: 0.8125rem;
861
+ }
862
+
863
+ .range-label {
864
+ font-size: 0.9375rem;
865
+ }
866
+
867
+ .scale-description {
868
+ font-size: 0.875rem;
869
+ }
870
+
871
+ .table-header {
872
+ display: none;
873
+ }
874
+
875
+ .table-row {
876
+ grid-template-columns: 1fr;
877
+ gap: 0.75rem;
878
+ padding: 1rem;
879
+ }
880
+
881
+ .col-preview {
882
+ display: flex;
883
+ align-items: center;
884
+ gap: 1rem;
885
+ }
886
+
887
+ .col-name,
888
+ .col-token,
889
+ .col-value {
890
+ font-family: var(--xz-font-family-mono, monospace);
891
+ font-size: 0.8125rem;
892
+ }
893
+
894
+ .token-button,
895
+ .value-button {
896
+ font-size: 0.75rem;
897
+ padding: 0.25rem 0.375rem;
898
+ }
899
+ }
900
+ </style>