@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,3 @@
1
+ declare const Themes: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type Themes = ReturnType<typeof Themes>;
3
+ export default Themes;
@@ -0,0 +1,622 @@
1
+ <script lang="ts">
2
+ import { typography } from '@fastwork/xosmoz-theme'
3
+ import type { TypographyToken } from '@fastwork/xosmoz-theme'
4
+ import { createClipboardHandler } from '../../utils/clipboard'
5
+
6
+ // Track copied state
7
+ let copiedVar = $state('')
8
+
9
+ // Sample text for previews
10
+ let sampleText = $state(
11
+ 'สวัสดี The quick brown fox jumps over the lazy dog',
12
+ )
13
+
14
+ // Create clipboard handler with state management
15
+ const copyToClipboard = createClipboardHandler(
16
+ () => copiedVar,
17
+ (val) => (copiedVar = val),
18
+ )
19
+
20
+ // Helper to convert camelCase to kebab-case
21
+ function toKebabCase(str: string) {
22
+ return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
23
+ }
24
+
25
+ // Token display type
26
+ interface TokenDisplay extends TypographyToken {
27
+ name: string
28
+ cssVar: string
29
+ }
30
+
31
+ // Helper to create token entry
32
+ function createToken(key: string, token: TypographyToken): TokenDisplay {
33
+ return {
34
+ name: key,
35
+ cssVar: `--xz-font-${toKebabCase(key)}`,
36
+ ...token,
37
+ }
38
+ }
39
+
40
+ // Typography groups for display (filtered from typography.font)
41
+ const typographyGroups = [
42
+ {
43
+ title: 'Headings',
44
+ description: 'Large display text for page and section headers.',
45
+ tokens: Object.entries(typography.font)
46
+ .filter(([key]) => key.startsWith('heading'))
47
+ .map(([key, token]) =>
48
+ createToken(key, token as TypographyToken),
49
+ ),
50
+ },
51
+ {
52
+ title: 'Titles',
53
+ description: 'Medium-sized text for card headers and subsections.',
54
+ tokens: Object.entries(typography.font)
55
+ .filter(([key]) => key.startsWith('title'))
56
+ .map(([key, token]) =>
57
+ createToken(key, token as TypographyToken),
58
+ ),
59
+ },
60
+ {
61
+ title: 'Subtitles',
62
+ description:
63
+ 'Supporting text with bold and regular weight variants.',
64
+ tokens: Object.entries(typography.font)
65
+ .filter(([key]) => key.startsWith('subtitle'))
66
+ .map(([key, token]) =>
67
+ createToken(key, token as TypographyToken),
68
+ ),
69
+ },
70
+ {
71
+ title: 'Body',
72
+ description: 'Standard text for paragraphs and general content.',
73
+ tokens: Object.entries(typography.font)
74
+ .filter(([key]) => key.startsWith('body'))
75
+ .map(([key, token]) =>
76
+ createToken(key, token as TypographyToken),
77
+ ),
78
+ },
79
+ ]
80
+ </script>
81
+
82
+ <div class="typography-page">
83
+ <div class="header">
84
+ <h1>Typography</h1>
85
+ <p class="lead">
86
+ Font shorthand tokens for the CSS <code>font</code> property. Each token
87
+ combines weight, size/line-height, and font-family.
88
+ </p>
89
+ <div class="usage-examples">
90
+ <span class="usage-label">CSS</span>
91
+ <pre class="usage-code"><code
92
+ >{`.element {
93
+ font: var(--xz-font-heading1);
94
+
95
+ font-size: var(--xz-font-size-400);
96
+ font-family: var(--xz-font-family-primary);
97
+ line-height: var(--xz-line-height-150pct);
98
+ }`}</code
99
+ ></pre>
100
+ <span class="usage-label">JS</span>
101
+ <pre class="usage-code"><code
102
+ >{`import { typography } from '@fastwork/xosmoz-theme';
103
+
104
+ typography.font.heading1;
105
+
106
+ typography.fontSize[400];
107
+ typography.fontFamily.primary;
108
+ typography.lineHeight['150pct'];`}</code
109
+ ></pre>
110
+ </div>
111
+ <div class="sample-text-input">
112
+ <label for="sample-text">Preview text</label>
113
+ <input
114
+ id="sample-text"
115
+ type="text"
116
+ bind:value={sampleText}
117
+ placeholder="Enter sample text..."
118
+ />
119
+ </div>
120
+ </div>
121
+
122
+ <!-- Typography Groups -->
123
+ {#each typographyGroups as group (group.title)}
124
+ <section class="typography-group">
125
+ <div class="group-header">
126
+ <h2>{group.title}</h2>
127
+ <span class="group-desc">{group.description}</span>
128
+ </div>
129
+
130
+ <div class="token-table">
131
+ <div class="table-header">
132
+ <div class="col-token">Token</div>
133
+ <div class="col-weight">Weight</div>
134
+ <div class="col-size">Desktop</div>
135
+ <div class="col-size">Mobile</div>
136
+ <div class="col-preview">Preview</div>
137
+ </div>
138
+
139
+ {#each group.tokens as token (token.name)}
140
+ <div class="table-row">
141
+ <div class="col-token">
142
+ <button
143
+ class="token-btn"
144
+ class:copied={copiedVar === token.cssVar}
145
+ onclick={() => copyToClipboard(token.cssVar)}
146
+ title="Click to copy"
147
+ >
148
+ {token.cssVar}
149
+ </button>
150
+ </div>
151
+ <div class="col-weight">{token.fontWeight}</div>
152
+ <div class="col-size">{token.fontSize.desktop}</div>
153
+ <div class="col-size">{token.fontSize.mobile}</div>
154
+ <div class="col-preview">
155
+ <span
156
+ class="preview-text"
157
+ style="font: {token.fontWeight} {token.fontSize
158
+ .desktop}/{token.lineHeight} {token.fontFamily};"
159
+ >
160
+ {sampleText}
161
+ </span>
162
+ </div>
163
+ </div>
164
+ {/each}
165
+ </div>
166
+ </section>
167
+ {/each}
168
+
169
+ <!-- Font Size Scale Reference -->
170
+ <section class="scale-section">
171
+ <h2>Font Size Scale</h2>
172
+ <p class="scale-desc">
173
+ Use <code>--xz-font-size-{'{key}'}</code> for individual font sizes.
174
+ </p>
175
+ <div class="scale-table">
176
+ <div class="scale-header">
177
+ <div class="scale-col-var">CSS Variable</div>
178
+ <div class="scale-col-value">Value</div>
179
+ <div class="scale-col-px">Pixels</div>
180
+ </div>
181
+ {#each Object.entries(typography.fontSize) as [key, value] (key)}
182
+ {@const cssVar = `--xz-font-size-${key}`}
183
+ {@const pxValue = parseFloat(value) * 16}
184
+ <div class="scale-row">
185
+ <button
186
+ class="scale-var"
187
+ class:copied={copiedVar === cssVar}
188
+ onclick={() => copyToClipboard(cssVar)}
189
+ title="Click to copy"
190
+ >
191
+ {cssVar}
192
+ </button>
193
+ <span class="scale-val">{value}</span>
194
+ <span class="scale-px">{pxValue}px</span>
195
+ </div>
196
+ {/each}
197
+ </div>
198
+ </section>
199
+
200
+ <!-- Line Height Reference -->
201
+ <section class="scale-section">
202
+ <h2>Line Height</h2>
203
+ <p class="scale-desc">
204
+ Use <code>--xz-line-height-{'{key}'}</code> for line heights.
205
+ </p>
206
+ <div class="scale-table">
207
+ <div class="scale-header">
208
+ <div class="scale-col-var">CSS Variable</div>
209
+ <div class="scale-col-value">Value</div>
210
+ </div>
211
+ {#each Object.entries(typography.lineHeight) as [key, value] (key)}
212
+ {@const cssVar = `--xz-line-height-${key}`}
213
+ <div class="scale-row">
214
+ <button
215
+ class="scale-var"
216
+ class:copied={copiedVar === cssVar}
217
+ onclick={() => copyToClipboard(cssVar)}
218
+ title="Click to copy"
219
+ >
220
+ {cssVar}
221
+ </button>
222
+ <span class="scale-val">{value}</span>
223
+ </div>
224
+ {/each}
225
+ </div>
226
+ </section>
227
+
228
+ <!-- Font Family Reference -->
229
+ <section class="scale-section">
230
+ <h2>Font Family</h2>
231
+ <p class="scale-desc">
232
+ Use <code>--xz-font-family-{'{key}'}</code> for font families.
233
+ </p>
234
+ <div class="font-family-list">
235
+ {#each Object.entries(typography.fontFamily) as [key, value] (key)}
236
+ {@const cssVar = `--xz-font-family-${key}`}
237
+ <div class="font-family-item">
238
+ <div class="font-family-header">
239
+ <button
240
+ class="scale-var"
241
+ class:copied={copiedVar === cssVar}
242
+ onclick={() => copyToClipboard(cssVar)}
243
+ title="Click to copy"
244
+ >
245
+ {cssVar}
246
+ </button>
247
+ </div>
248
+ <p
249
+ class="font-family-preview"
250
+ style="font-family: {value};"
251
+ >
252
+ {sampleText}
253
+ </p>
254
+ <code class="font-family-value">{value}</code>
255
+ </div>
256
+ {/each}
257
+ </div>
258
+ </section>
259
+ </div>
260
+
261
+ <style>
262
+ .typography-page {
263
+ background-color: var(--xz-color-bg-100);
264
+ font-family: var(--xz-font-family-primary, system-ui);
265
+ padding: 1.5rem;
266
+ max-width: 100%;
267
+ }
268
+
269
+ .header {
270
+ margin-bottom: 2rem;
271
+ }
272
+
273
+ h1 {
274
+ font-size: 2rem;
275
+ font-weight: 700;
276
+ margin-bottom: 0.5rem;
277
+ color: var(--xz-color-text);
278
+ }
279
+
280
+ h2 {
281
+ font-size: 1.125rem;
282
+ font-weight: 600;
283
+ color: var(--xz-color-text);
284
+ margin: 0;
285
+ }
286
+
287
+ .lead {
288
+ font-size: 0.9375rem;
289
+ color: var(--xz-color-content-200);
290
+ line-height: 1.5;
291
+ margin-bottom: 0.75rem;
292
+ }
293
+
294
+ .lead code {
295
+ background: var(--xz-color-bg-300);
296
+ padding: 0.125rem 0.375rem;
297
+ border-radius: 0.25rem;
298
+ font-family: var(--xz-font-family-mono, monospace);
299
+ font-size: 0.875em;
300
+ }
301
+
302
+ .usage-examples {
303
+ display: flex;
304
+ flex-direction: column;
305
+ gap: 0.5rem;
306
+ margin-top: 0.75rem;
307
+ }
308
+
309
+ .usage-label {
310
+ font-size: 0.5625rem;
311
+ font-weight: 600;
312
+ text-transform: uppercase;
313
+ letter-spacing: 0.05em;
314
+ color: var(--xz-color-content-200);
315
+ background: var(--xz-color-bg-300);
316
+ padding: 0.125rem 0.375rem;
317
+ border-radius: 0.25rem;
318
+ align-self: flex-start;
319
+ }
320
+
321
+ .usage-code {
322
+ margin: 0 0 0.5rem 0;
323
+ background: var(--xz-color-bg-200);
324
+ padding: 0.625rem 0.875rem;
325
+ border-radius: 0.25rem;
326
+ overflow-x: auto;
327
+ }
328
+
329
+ .usage-code code {
330
+ font-family: var(--xz-font-family-mono, monospace);
331
+ font-size: 0.8125rem;
332
+ color: var(--xz-color-content-100);
333
+ line-height: 1.6;
334
+ white-space: pre;
335
+ }
336
+
337
+ /* Sample Text Input */
338
+ .sample-text-input {
339
+ display: flex;
340
+ align-items: center;
341
+ gap: 0.75rem;
342
+ margin-top: 1rem;
343
+ }
344
+
345
+ .sample-text-input label {
346
+ font-size: 0.8125rem;
347
+ font-weight: 500;
348
+ color: var(--xz-color-content-200);
349
+ white-space: nowrap;
350
+ }
351
+
352
+ .sample-text-input input {
353
+ flex: 1;
354
+ max-width: 400px;
355
+ padding: 0.5rem 0.75rem;
356
+ font-size: 0.875rem;
357
+ font-family: inherit;
358
+ color: var(--xz-color-text);
359
+ background: var(--xz-color-bg-200);
360
+ border: 1px solid var(--xz-color-line-100);
361
+ border-radius: 0.375rem;
362
+ outline: none;
363
+ transition:
364
+ border-color 0.15s,
365
+ box-shadow 0.15s;
366
+ }
367
+
368
+ .sample-text-input input:focus {
369
+ border-color: var(--xz-color-primary-content-100);
370
+ box-shadow: 0 0 0 2px
371
+ var(--xz-color-primary-bg-100, rgba(59, 130, 246, 0.2));
372
+ }
373
+
374
+ .sample-text-input input::placeholder {
375
+ color: var(--xz-color-content-200);
376
+ opacity: 0.6;
377
+ }
378
+
379
+ /* Typography Groups */
380
+ .typography-group {
381
+ margin-bottom: 2rem;
382
+ }
383
+
384
+ .group-header {
385
+ display: flex;
386
+ align-items: baseline;
387
+ gap: 0.75rem;
388
+ margin-bottom: 0.75rem;
389
+ }
390
+
391
+ .group-desc {
392
+ font-size: 0.8125rem;
393
+ color: var(--xz-color-content-200);
394
+ }
395
+
396
+ /* Token Table */
397
+ .token-table {
398
+ border: 1px solid var(--xz-color-line-100);
399
+ border-radius: 0.375rem;
400
+ overflow: hidden;
401
+ }
402
+
403
+ .table-header {
404
+ display: grid;
405
+ grid-template-columns: 240px 60px 70px 70px minmax(0, 1fr);
406
+ gap: 0.75rem;
407
+ padding: 0.5rem 0.75rem;
408
+ background: var(--xz-color-bg-300);
409
+ font-size: 0.6875rem;
410
+ font-weight: 600;
411
+ color: var(--xz-color-content-200);
412
+ text-transform: uppercase;
413
+ letter-spacing: 0.05em;
414
+ }
415
+
416
+ .table-row {
417
+ display: grid;
418
+ grid-template-columns: 240px 60px 70px 70px minmax(0, 1fr);
419
+ gap: 0.75rem;
420
+ padding: 0.5rem 0.75rem;
421
+ align-items: center;
422
+ border-top: 1px solid var(--xz-color-line-100);
423
+ transition: background-color 0.1s;
424
+ }
425
+
426
+ .table-row:hover {
427
+ background: var(--xz-color-bg-200);
428
+ }
429
+
430
+ .col-weight,
431
+ .col-size {
432
+ font-family: var(--xz-font-family-mono, monospace);
433
+ font-size: 0.8125rem;
434
+ color: var(--xz-color-content-100);
435
+ }
436
+
437
+ .col-preview {
438
+ overflow-x: auto;
439
+ max-width: 100%;
440
+ }
441
+
442
+ .token-btn {
443
+ background: none;
444
+ border: none;
445
+ padding: 0.25rem 0.5rem;
446
+ margin-left: -0.5rem;
447
+ font-family: var(--xz-font-family-mono, monospace);
448
+ font-size: 0.8125rem;
449
+ font-weight: 500;
450
+ color: var(--xz-color-primary-content-100);
451
+ cursor: pointer;
452
+ border-radius: 0.25rem;
453
+ transition: all 0.1s;
454
+ text-align: left;
455
+ }
456
+
457
+ .token-btn:hover {
458
+ background: var(--xz-color-bg-300);
459
+ }
460
+
461
+ .token-btn.copied {
462
+ background: var(--xz-color-success-bg-100, #22c55e);
463
+ color: white;
464
+ }
465
+
466
+ .preview-text {
467
+ color: var(--xz-color-text);
468
+ white-space: nowrap;
469
+ display: block;
470
+ }
471
+
472
+ /* Scale Section */
473
+ .scale-section {
474
+ margin-top: 2rem;
475
+ }
476
+
477
+ .scale-section h2 {
478
+ margin-bottom: 0.5rem;
479
+ }
480
+
481
+ .scale-desc {
482
+ font-size: 0.8125rem;
483
+ color: var(--xz-color-content-200);
484
+ margin-bottom: 0.75rem;
485
+ }
486
+
487
+ .scale-desc code {
488
+ background: var(--xz-color-bg-300);
489
+ padding: 0.125rem 0.375rem;
490
+ border-radius: 0.25rem;
491
+ font-family: var(--xz-font-family-mono, monospace);
492
+ font-size: 0.875em;
493
+ }
494
+
495
+ .scale-table {
496
+ border: 1px solid var(--xz-color-line-100);
497
+ border-radius: 0.375rem;
498
+ overflow: hidden;
499
+ }
500
+
501
+ .scale-header {
502
+ display: grid;
503
+ grid-template-columns: 1fr 80px 60px;
504
+ gap: 0.75rem;
505
+ padding: 0.5rem 0.75rem;
506
+ background: var(--xz-color-bg-300);
507
+ font-size: 0.6875rem;
508
+ font-weight: 600;
509
+ color: var(--xz-color-content-200);
510
+ text-transform: uppercase;
511
+ letter-spacing: 0.05em;
512
+ }
513
+
514
+ .scale-row {
515
+ display: grid;
516
+ grid-template-columns: 1fr 80px 60px;
517
+ gap: 0.75rem;
518
+ padding: 0.375rem 0.75rem;
519
+ align-items: center;
520
+ border-top: 1px solid var(--xz-color-line-100);
521
+ }
522
+
523
+ .scale-row:hover {
524
+ background: var(--xz-color-bg-200);
525
+ }
526
+
527
+ .scale-var {
528
+ background: none;
529
+ border: none;
530
+ padding: 0.25rem 0.5rem;
531
+ margin-left: -0.5rem;
532
+ font-family: var(--xz-font-family-mono, monospace);
533
+ font-size: 0.8125rem;
534
+ color: var(--xz-color-primary-content-100);
535
+ cursor: pointer;
536
+ border-radius: 0.25rem;
537
+ transition: all 0.1s;
538
+ text-align: left;
539
+ }
540
+
541
+ .scale-var:hover {
542
+ background: var(--xz-color-bg-300);
543
+ }
544
+
545
+ .scale-var.copied {
546
+ background: var(--xz-color-success-bg-100, #22c55e);
547
+ color: white;
548
+ }
549
+
550
+ .scale-val,
551
+ .scale-px {
552
+ font-family: var(--xz-font-family-mono, monospace);
553
+ font-size: 0.8125rem;
554
+ color: var(--xz-color-content-100);
555
+ }
556
+
557
+ /* Font Family Section */
558
+ .font-family-list {
559
+ display: flex;
560
+ flex-direction: column;
561
+ gap: 1rem;
562
+ }
563
+
564
+ .font-family-item {
565
+ border: 1px solid var(--xz-color-line-100);
566
+ border-radius: 0.375rem;
567
+ overflow: hidden;
568
+ }
569
+
570
+ .font-family-header {
571
+ padding: 0.5rem 0.75rem;
572
+ background: var(--xz-color-bg-300);
573
+ }
574
+
575
+ .font-family-preview {
576
+ padding: 0.75rem;
577
+ font-size: 1.25rem;
578
+ color: var(--xz-color-text);
579
+ margin: 0;
580
+ }
581
+
582
+ .font-family-value {
583
+ display: block;
584
+ padding: 0.5rem 0.75rem;
585
+ font-family: var(--xz-font-family-mono, monospace);
586
+ font-size: 0.75rem;
587
+ color: var(--xz-color-content-200);
588
+ background: var(--xz-color-bg-200);
589
+ word-break: break-all;
590
+ }
591
+
592
+ /* Responsive */
593
+ @media (max-width: 900px) {
594
+ .table-header {
595
+ display: none;
596
+ }
597
+
598
+ .table-row {
599
+ grid-template-columns: 1fr;
600
+ gap: 0.25rem;
601
+ padding: 0.75rem;
602
+ }
603
+
604
+ .col-token {
605
+ order: 1;
606
+ }
607
+
608
+ .col-preview {
609
+ order: 2;
610
+ margin-top: 0.25rem;
611
+ }
612
+
613
+ .col-weight,
614
+ .col-size {
615
+ display: none;
616
+ }
617
+
618
+ .token-btn {
619
+ margin-left: 0;
620
+ }
621
+ }
622
+ </style>
@@ -0,0 +1,3 @@
1
+ declare const Typography: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type Typography = ReturnType<typeof Typography>;
3
+ export default Typography;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @fastwork/xosmoz-svelte
3
+ * Svelte 5 components for the Xosmoz design system
4
+ *
5
+ * @example
6
+ * ```svelte
7
+ * <script>
8
+ * import { Button } from '@fastwork/xosmoz-svelte';
9
+ * </script>
10
+ *
11
+ * <Button variant="primary" size="medium">Click me</Button>
12
+ * ```
13
+ *
14
+ * @example CSS Setup
15
+ * ```ts
16
+ * // In your app's entry point or layout
17
+ * import '@fastwork/xosmoz-svelte/styles.css';
18
+ * ```
19
+ */
20
+ export { default as Button } from './components/Button.svelte';
21
+ export type { ButtonProps, ButtonVariant, ButtonSize } from './components/Button.svelte';
package/dist/index.js ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @fastwork/xosmoz-svelte
3
+ * Svelte 5 components for the Xosmoz design system
4
+ *
5
+ * @example
6
+ * ```svelte
7
+ * <script>
8
+ * import { Button } from '@fastwork/xosmoz-svelte';
9
+ * </script>
10
+ *
11
+ * <Button variant="primary" size="medium">Click me</Button>
12
+ * ```
13
+ *
14
+ * @example CSS Setup
15
+ * ```ts
16
+ * // In your app's entry point or layout
17
+ * import '@fastwork/xosmoz-svelte/styles.css';
18
+ * ```
19
+ */
20
+ // Components
21
+ export { default as Button } from './components/Button.svelte';
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Xosmoz Svelte Component Styles
3
+ *
4
+ * This file imports all required styles for xosmoz-svelte components.
5
+ *
6
+ * Usage in your app:
7
+ *
8
+ * Option 1: Import this file (includes theme and component styles)
9
+ * import '@fastwork/xosmoz-svelte/styles.css';
10
+ *
11
+ * Option 2: Import individually for more control
12
+ * import '@fastwork/xosmoz-theme/base.css';
13
+ * import '@fastwork/xosmoz-theme/themes.css';
14
+ * import '@fastwork/xosmoz-css/component.css';
15
+ */
16
+
17
+ /* Base styles and design tokens */
18
+ @import '@fastwork/xosmoz-theme/base.css';
19
+
20
+ /* Theme CSS (light & dark mode support) */
21
+ @import '@fastwork/xosmoz-theme/themes.css';
22
+
23
+ /* Component styles */
24
+ @import '@fastwork/xosmoz-css/component.css';
25
+