@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.
- package/README.md +159 -0
- package/dist/assets/favicon.svg +1 -0
- package/dist/components/Button.svelte +118 -0
- package/dist/components/Button.svelte.d.ts +36 -0
- package/dist/docs/GettingStarted.mdx +190 -0
- package/dist/docs/Welcome.mdx +161 -0
- package/dist/docs/foundation/APCA.svelte +358 -0
- package/dist/docs/foundation/APCA.svelte.d.ts +18 -0
- package/dist/docs/foundation/BoxShadows.svelte +204 -0
- package/dist/docs/foundation/BoxShadows.svelte.d.ts +3 -0
- package/dist/docs/foundation/Colors.svelte +900 -0
- package/dist/docs/foundation/Colors.svelte.d.ts +3 -0
- package/dist/docs/foundation/KitchenSink.svelte +1581 -0
- package/dist/docs/foundation/KitchenSink.svelte.d.ts +18 -0
- package/dist/docs/foundation/Themes.svelte +1048 -0
- package/dist/docs/foundation/Themes.svelte.d.ts +3 -0
- package/dist/docs/foundation/Typography.svelte +622 -0
- package/dist/docs/foundation/Typography.svelte.d.ts +3 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +21 -0
- package/dist/styles.css +25 -0
- package/dist/utils/clipboard.d.ts +31 -0
- package/dist/utils/clipboard.js +57 -0
- package/dist/utils/colorCalculations.d.ts +32 -0
- package/dist/utils/colorCalculations.js +69 -0
- package/dist/utils/themeDetection.d.ts +20 -0
- package/dist/utils/themeDetection.js +48 -0
- package/package.json +89 -0
|
@@ -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>
|