shru-design-system 0.1.1 → 0.1.3

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/scripts/init.js CHANGED
@@ -1,14 +1,33 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * shru-design-system init script
4
+ * Design system init script
5
5
  * Sets up Tailwind CSS and required configuration
6
+ * This file is part of the design system library
6
7
  */
7
8
 
8
9
  const fs = require('fs');
9
10
  const path = require('path');
10
11
  const { execSync } = require('child_process');
11
12
 
13
+ // Get package name dynamically from package.json
14
+ function getPackageName() {
15
+ try {
16
+ const packageJsonPath = path.join(__dirname, '..', 'package.json');
17
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
18
+ return packageJson.name || 'shru-design-system';
19
+ } catch (error) {
20
+ // Fallback if package.json can't be read
21
+ return 'shru-design-system';
22
+ }
23
+ }
24
+
25
+ const PACKAGE_NAME = getPackageName();
26
+ const LIBRARY_NAME = `${PACKAGE_NAME} library`;
27
+
28
+ // Configuration constants
29
+ const TAILWIND_VERSION = '^3.4.0';
30
+
12
31
  const colors = {
13
32
  reset: '\x1b[0m',
14
33
  green: '\x1b[32m',
@@ -49,8 +68,8 @@ function createTailwindConfig() {
49
68
  const existing = fs.readFileSync(configPath, 'utf8');
50
69
 
51
70
  // Check if our config is already there
52
- if (existing.includes('shru-design-system')) {
53
- log('Configuration already includes shru-design-system setup.', 'green');
71
+ if (existing.includes(PACKAGE_NAME)) {
72
+ log(`Configuration already includes ${PACKAGE_NAME} setup.`, 'green');
54
73
  return;
55
74
  }
56
75
 
@@ -60,11 +79,12 @@ function createTailwindConfig() {
60
79
  }
61
80
 
62
81
  const config = `/** @type {import('tailwindcss').Config} */
82
+ // This file was created by ${LIBRARY_NAME}
63
83
  export default {
64
84
  content: [
65
85
  "./index.html",
66
86
  "./src/**/*.{js,ts,jsx,tsx}",
67
- "./node_modules/shru-design-system/dist/**/*.{js,mjs}",
87
+ "./node_modules/${PACKAGE_NAME}/dist/**/*.{js,mjs}",
68
88
  ],
69
89
  theme: {
70
90
  extend: {
@@ -104,6 +124,10 @@ export default {
104
124
  md: "calc(var(--radius) - 2px)",
105
125
  sm: "calc(var(--radius) - 4px)",
106
126
  },
127
+ fontFamily: {
128
+ sans: ["var(--font-sans)", "system-ui", "sans-serif"],
129
+ body: ["var(--font-body)", "var(--font-sans)", "system-ui", "sans-serif"],
130
+ },
107
131
  },
108
132
  },
109
133
  plugins: [],
@@ -122,7 +146,8 @@ function createPostCSSConfig() {
122
146
  return;
123
147
  }
124
148
 
125
- const config = `export default {
149
+ const config = `// This file was created by ${LIBRARY_NAME}
150
+ export default {
126
151
  plugins: {
127
152
  tailwindcss: {},
128
153
  autoprefixer: {},
@@ -155,7 +180,7 @@ function createCSSFile() {
155
180
  // Append to existing file
156
181
  const cssVars = `
157
182
 
158
- /* shru-design-system CSS variables */
183
+ /* Design system CSS variables - Created by ${LIBRARY_NAME} */
159
184
  @layer base {
160
185
  :root {
161
186
  --background: 0 0% 100%;
@@ -197,6 +222,7 @@ function createCSSFile() {
197
222
  @tailwind components;
198
223
  @tailwind utilities;
199
224
 
225
+ /* This file was created by ${LIBRARY_NAME} */
200
226
  @layer base {
201
227
  :root {
202
228
  --background: 0 0% 100%;
@@ -234,6 +260,410 @@ function createCSSFile() {
234
260
  log('Created src/index.css', 'green');
235
261
  }
236
262
 
263
+ function createTokenFiles() {
264
+ const publicDir = path.join(process.cwd(), 'public');
265
+ const tokensDir = path.join(publicDir, 'tokens');
266
+ const themesDir = path.join(tokensDir, 'themes');
267
+
268
+ // Create directories
269
+ if (!fs.existsSync(publicDir)) {
270
+ fs.mkdirSync(publicDir, { recursive: true });
271
+ }
272
+ if (!fs.existsSync(tokensDir)) {
273
+ fs.mkdirSync(tokensDir, { recursive: true });
274
+ }
275
+ if (!fs.existsSync(themesDir)) {
276
+ fs.mkdirSync(themesDir, { recursive: true });
277
+ }
278
+
279
+ // Create base.json
280
+ const basePath = path.join(tokensDir, 'base.json');
281
+ if (!fs.existsSync(basePath)) {
282
+ const baseJson = {
283
+ "_createdBy": LIBRARY_NAME,
284
+ "color": {
285
+ "primary": "{palette.blue.500}",
286
+ "primary-hover": "{palette.blue.600}",
287
+ "primary-foreground": "{palette.white}",
288
+ "secondary": "{palette.gray.100}",
289
+ "secondary-foreground": "{palette.gray.900}",
290
+ "background": "{palette.white}",
291
+ "foreground": "{palette.gray.900}",
292
+ "card": "{palette.white}",
293
+ "card-foreground": "{palette.gray.900}",
294
+ "popover": "{palette.white}",
295
+ "popover-foreground": "{palette.gray.900}",
296
+ "muted": "{palette.gray.100}",
297
+ "muted-foreground": "{palette.gray.500}",
298
+ "accent": "{palette.gray.100}",
299
+ "accent-foreground": "{palette.gray.900}",
300
+ "destructive": "{palette.red.500}",
301
+ "destructive-foreground": "{palette.white}",
302
+ "border": "{palette.gray.200}",
303
+ "input": "{palette.gray.200}",
304
+ "ring": "{palette.gray.400}",
305
+ "skeleton": "{palette.gray.200}"
306
+ },
307
+ "spacing": {
308
+ "component": {
309
+ "xs": "0.25rem",
310
+ "sm": "0.5rem",
311
+ "md": "1rem",
312
+ "lg": "1.5rem",
313
+ "xl": "2rem"
314
+ },
315
+ "base": "0.25rem"
316
+ },
317
+ "typography": {
318
+ "font": {
319
+ "body": "var(--font-sans)",
320
+ "sans": "var(--font-sans)",
321
+ "mono": "var(--font-mono)"
322
+ }
323
+ },
324
+ "shape": {
325
+ "radius": {
326
+ "button": "0.375rem",
327
+ "card": "0.5rem",
328
+ "input": "0.375rem"
329
+ }
330
+ }
331
+ };
332
+ fs.writeFileSync(basePath, JSON.stringify(baseJson, null, 2));
333
+ log('Created public/tokens/base.json', 'green');
334
+ } else {
335
+ log('public/tokens/base.json already exists. Skipping...', 'yellow');
336
+ }
337
+
338
+ // Create palettes.json
339
+ const palettesPath = path.join(tokensDir, 'palettes.json');
340
+ if (!fs.existsSync(palettesPath)) {
341
+ const palettesJson = {
342
+ "_createdBy": LIBRARY_NAME,
343
+ "palette": {
344
+ "white": "#ffffff",
345
+ "black": "#000000",
346
+ "transparent": "transparent",
347
+ "gray": {
348
+ "50": "#f9fafb",
349
+ "100": "#f3f4f6",
350
+ "200": "#e5e7eb",
351
+ "300": "#d1d5db",
352
+ "400": "#9ca3af",
353
+ "500": "#6b7280",
354
+ "600": "#4b5563",
355
+ "700": "#374151",
356
+ "800": "#1f2937",
357
+ "900": "#111827",
358
+ "950": "#030712"
359
+ },
360
+ "blue": {
361
+ "50": "#eff6ff",
362
+ "100": "#dbeafe",
363
+ "200": "#bfdbfe",
364
+ "300": "#93c5fd",
365
+ "400": "#60a5fa",
366
+ "500": "#3b82f6",
367
+ "600": "#2563eb",
368
+ "700": "#1d4ed8",
369
+ "800": "#1e40af",
370
+ "900": "#1e3a8a",
371
+ "950": "#172554"
372
+ },
373
+ "red": {
374
+ "50": "#fef2f2",
375
+ "100": "#fee2e2",
376
+ "200": "#fecaca",
377
+ "300": "#fca5a5",
378
+ "400": "#f87171",
379
+ "500": "#ef4444",
380
+ "600": "#dc2626",
381
+ "700": "#b91c1c",
382
+ "800": "#991b1b",
383
+ "900": "#7f1d1d",
384
+ "950": "#450a0a"
385
+ }
386
+ }
387
+ };
388
+ fs.writeFileSync(palettesPath, JSON.stringify(palettesJson, null, 2));
389
+ log('Created public/tokens/palettes.json', 'green');
390
+ } else {
391
+ log('public/tokens/palettes.json already exists. Skipping...', 'yellow');
392
+ }
393
+
394
+ // Create theme directories and files
395
+ const themeCategories = ['color', 'typography', 'shape', 'density', 'animation', 'custom'];
396
+
397
+ themeCategories.forEach(category => {
398
+ const categoryDir = path.join(themesDir, category);
399
+ if (!fs.existsSync(categoryDir)) {
400
+ fs.mkdirSync(categoryDir, { recursive: true });
401
+ }
402
+ });
403
+
404
+ // Create color/white.json
405
+ const whiteThemePath = path.join(themesDir, 'color', 'white.json');
406
+ if (!fs.existsSync(whiteThemePath)) {
407
+ const whiteTheme = {
408
+ "_createdBy": LIBRARY_NAME,
409
+ "color": {
410
+ "primary": "{palette.blue.500}",
411
+ "primary-foreground": "{palette.white}",
412
+ "background": "{palette.white}",
413
+ "foreground": "{palette.gray.900}",
414
+ "card": "{palette.white}",
415
+ "card-foreground": "{palette.gray.900}",
416
+ "popover": "{palette.white}",
417
+ "popover-foreground": "{palette.gray.900}",
418
+ "secondary": "{palette.gray.100}",
419
+ "secondary-foreground": "{palette.gray.900}",
420
+ "muted": "{palette.gray.100}",
421
+ "muted-foreground": "{palette.gray.500}",
422
+ "accent": "{palette.gray.100}",
423
+ "accent-foreground": "{palette.gray.900}",
424
+ "destructive": "{palette.red.500}",
425
+ "destructive-foreground": "{palette.white}",
426
+ "border": "{palette.gray.200}",
427
+ "input": "{palette.gray.200}",
428
+ "ring": "{palette.gray.400}",
429
+ "skeleton": "{palette.gray.200}"
430
+ }
431
+ };
432
+ fs.writeFileSync(whiteThemePath, JSON.stringify(whiteTheme, null, 2));
433
+ log('Created public/tokens/themes/color/white.json', 'green');
434
+ }
435
+
436
+ // Create color/dark.json
437
+ const darkThemePath = path.join(themesDir, 'color', 'dark.json');
438
+ if (!fs.existsSync(darkThemePath)) {
439
+ const darkTheme = {
440
+ "_createdBy": LIBRARY_NAME,
441
+ "color": {
442
+ "primary": "{palette.blue.400}",
443
+ "primary-foreground": "{palette.gray.900}",
444
+ "background": "{palette.gray.900}",
445
+ "foreground": "{palette.gray.50}",
446
+ "card": "{palette.gray.800}",
447
+ "card-foreground": "{palette.gray.50}",
448
+ "popover": "{palette.gray.800}",
449
+ "popover-foreground": "{palette.gray.50}",
450
+ "secondary": "{palette.gray.800}",
451
+ "secondary-foreground": "{palette.gray.50}",
452
+ "muted": "{palette.gray.800}",
453
+ "muted-foreground": "{palette.gray.400}",
454
+ "accent": "{palette.gray.800}",
455
+ "accent-foreground": "{palette.gray.50}",
456
+ "destructive": "{palette.red.500}",
457
+ "destructive-foreground": "{palette.white}",
458
+ "border": "{palette.gray.700}",
459
+ "input": "{palette.gray.700}",
460
+ "ring": "{palette.gray.600}",
461
+ "skeleton": "{palette.gray.700}"
462
+ }
463
+ };
464
+ fs.writeFileSync(darkThemePath, JSON.stringify(darkTheme, null, 2));
465
+ log('Created public/tokens/themes/color/dark.json', 'green');
466
+ }
467
+
468
+ // Create typography/sans.json
469
+ const sansThemePath = path.join(themesDir, 'typography', 'sans.json');
470
+ if (!fs.existsSync(sansThemePath)) {
471
+ const sansTheme = {
472
+ "_createdBy": LIBRARY_NAME,
473
+ "typography": {
474
+ "font": {
475
+ "body": "system-ui, -apple-system, sans-serif",
476
+ "sans": "system-ui, -apple-system, sans-serif"
477
+ }
478
+ }
479
+ };
480
+ fs.writeFileSync(sansThemePath, JSON.stringify(sansTheme, null, 2));
481
+ log('Created public/tokens/themes/typography/sans.json', 'green');
482
+ }
483
+
484
+ // Create typography/serif.json
485
+ const serifThemePath = path.join(themesDir, 'typography', 'serif.json');
486
+ if (!fs.existsSync(serifThemePath)) {
487
+ const serifTheme = {
488
+ "_createdBy": LIBRARY_NAME,
489
+ "typography": {
490
+ "font": {
491
+ "body": "Georgia, serif",
492
+ "sans": "Georgia, serif"
493
+ }
494
+ }
495
+ };
496
+ fs.writeFileSync(serifThemePath, JSON.stringify(serifTheme, null, 2));
497
+ log('Created public/tokens/themes/typography/serif.json', 'green');
498
+ }
499
+
500
+ // Create shape/smooth.json
501
+ const smoothThemePath = path.join(themesDir, 'shape', 'smooth.json');
502
+ if (!fs.existsSync(smoothThemePath)) {
503
+ const smoothTheme = {
504
+ "_createdBy": LIBRARY_NAME,
505
+ "shape": {
506
+ "radius": {
507
+ "button": "0.5rem",
508
+ "card": "0.75rem",
509
+ "input": "0.5rem"
510
+ }
511
+ }
512
+ };
513
+ fs.writeFileSync(smoothThemePath, JSON.stringify(smoothTheme, null, 2));
514
+ log('Created public/tokens/themes/shape/smooth.json', 'green');
515
+ }
516
+
517
+ // Create shape/sharp.json
518
+ const sharpThemePath = path.join(themesDir, 'shape', 'sharp.json');
519
+ if (!fs.existsSync(sharpThemePath)) {
520
+ const sharpTheme = {
521
+ "_createdBy": LIBRARY_NAME,
522
+ "shape": {
523
+ "radius": {
524
+ "button": "0",
525
+ "card": "0",
526
+ "input": "0"
527
+ }
528
+ }
529
+ };
530
+ fs.writeFileSync(sharpThemePath, JSON.stringify(sharpTheme, null, 2));
531
+ log('Created public/tokens/themes/shape/sharp.json', 'green');
532
+ }
533
+
534
+ // Create density/comfortable.json
535
+ const comfortableThemePath = path.join(themesDir, 'density', 'comfortable.json');
536
+ if (!fs.existsSync(comfortableThemePath)) {
537
+ const comfortableTheme = {
538
+ "_createdBy": LIBRARY_NAME,
539
+ "spacing": {
540
+ "component": {
541
+ "xs": "0.5rem",
542
+ "sm": "0.75rem",
543
+ "md": "1.25rem",
544
+ "lg": "2rem",
545
+ "xl": "2.5rem"
546
+ }
547
+ }
548
+ };
549
+ fs.writeFileSync(comfortableThemePath, JSON.stringify(comfortableTheme, null, 2));
550
+ log('Created public/tokens/themes/density/comfortable.json', 'green');
551
+ }
552
+
553
+ // Create density/compact.json
554
+ const compactThemePath = path.join(themesDir, 'density', 'compact.json');
555
+ if (!fs.existsSync(compactThemePath)) {
556
+ const compactTheme = {
557
+ "_createdBy": LIBRARY_NAME,
558
+ "spacing": {
559
+ "component": {
560
+ "xs": "0.25rem",
561
+ "sm": "0.5rem",
562
+ "md": "0.75rem",
563
+ "lg": "1rem",
564
+ "xl": "1.5rem"
565
+ }
566
+ }
567
+ };
568
+ fs.writeFileSync(compactThemePath, JSON.stringify(compactTheme, null, 2));
569
+ log('Created public/tokens/themes/density/compact.json', 'green');
570
+ }
571
+
572
+ // Create animation/gentle.json
573
+ const gentleThemePath = path.join(themesDir, 'animation', 'gentle.json');
574
+ if (!fs.existsSync(gentleThemePath)) {
575
+ const gentleTheme = {
576
+ "_createdBy": LIBRARY_NAME,
577
+ "animation": {
578
+ "duration": {
579
+ "fast": "150ms",
580
+ "normal": "300ms",
581
+ "slow": "500ms"
582
+ }
583
+ }
584
+ };
585
+ fs.writeFileSync(gentleThemePath, JSON.stringify(gentleTheme, null, 2));
586
+ log('Created public/tokens/themes/animation/gentle.json', 'green');
587
+ }
588
+
589
+ // Create animation/brisk.json
590
+ const briskThemePath = path.join(themesDir, 'animation', 'brisk.json');
591
+ if (!fs.existsSync(briskThemePath)) {
592
+ const briskTheme = {
593
+ "_createdBy": LIBRARY_NAME,
594
+ "animation": {
595
+ "duration": {
596
+ "fast": "100ms",
597
+ "normal": "200ms",
598
+ "slow": "300ms"
599
+ }
600
+ }
601
+ };
602
+ fs.writeFileSync(briskThemePath, JSON.stringify(briskTheme, null, 2));
603
+ log('Created public/tokens/themes/animation/brisk.json', 'green');
604
+ }
605
+ }
606
+
607
+ function injectThemeScript() {
608
+ const scriptPath = path.join(__dirname, 'apply-theme-sync.js');
609
+ const scriptContent = fs.readFileSync(scriptPath, 'utf8');
610
+
611
+ // Try to find and update index.html
612
+ const htmlPaths = [
613
+ path.join(process.cwd(), 'index.html'),
614
+ path.join(process.cwd(), 'public', 'index.html'),
615
+ ];
616
+
617
+ for (const htmlPath of htmlPaths) {
618
+ if (fs.existsSync(htmlPath)) {
619
+ let htmlContent = fs.readFileSync(htmlPath, 'utf8');
620
+
621
+ // Check if script is already injected
622
+ if (htmlContent.includes('dynamic-theme') || htmlContent.includes('apply-theme-sync')) {
623
+ log('Theme sync script already in index.html', 'green');
624
+ return;
625
+ }
626
+
627
+ // Add resource hints for token files (helps browser preload)
628
+ // Preload critical token files that are always needed
629
+ const preloadLinks = ` <!-- Preload token files for faster theme application - Created by ${LIBRARY_NAME} -->
630
+ <link rel="preload" href="/tokens/base.json" as="fetch" crossorigin>
631
+ <link rel="preload" href="/tokens/palettes.json" as="fetch" crossorigin>
632
+ <link rel="preload" href="/tokens/themes/color/white.json" as="fetch" crossorigin>
633
+ <link rel="preload" href="/tokens/themes/color/dark.json" as="fetch" crossorigin>`;
634
+
635
+ // Inject blocking script in <head> (runs before React)
636
+ const scriptTag = ` <!-- Blocking theme script to prevent flash - Created by ${LIBRARY_NAME} -->
637
+ <script>${scriptContent}</script>`;
638
+
639
+ if (htmlContent.includes('</head>')) {
640
+ // Add preload links and script before </head>
641
+ htmlContent = htmlContent.replace('</head>', `${preloadLinks}\n${scriptTag}\n</head>`);
642
+ fs.writeFileSync(htmlPath, htmlContent);
643
+ log('Injected theme sync script and preload links into index.html', 'green');
644
+ return;
645
+ } else if (htmlContent.includes('<head>')) {
646
+ // If no closing </head>, try to add after <head>
647
+ htmlContent = htmlContent.replace('<head>', `<head>\n${preloadLinks}\n${scriptTag}`);
648
+ fs.writeFileSync(htmlPath, htmlContent);
649
+ log('Injected theme sync script and preload links into index.html', 'green');
650
+ return;
651
+ } else if (htmlContent.includes('</body>')) {
652
+ // Last resort: add before </body>
653
+ htmlContent = htmlContent.replace('</body>', `${preloadLinks}\n${scriptTag}\n</body>`);
654
+ fs.writeFileSync(htmlPath, htmlContent);
655
+ log('Injected theme sync script and preload links into index.html (before </body>)', 'yellow');
656
+ return;
657
+ }
658
+ }
659
+ }
660
+
661
+ log('⚠️ Could not find index.html. Add these to <head> manually:', 'yellow');
662
+ log(' <link rel="preload" href="/tokens/base.json" as="fetch" crossorigin>', 'blue');
663
+ log(' <link rel="preload" href="/tokens/palettes.json" as="fetch" crossorigin>', 'blue');
664
+ log(' <script>/* apply-theme-sync.js content */</script>', 'blue');
665
+ }
666
+
237
667
  function checkMainFile() {
238
668
  const possiblePaths = [
239
669
  path.join(process.cwd(), 'src', 'main.tsx'),
@@ -263,12 +693,12 @@ function checkMainFile() {
263
693
 
264
694
  // Main execution
265
695
  function main() {
266
- log('\n🚀 Setting up shru-design-system...\n', 'blue');
696
+ log(`\n🚀 Setting up ${PACKAGE_NAME}...\n`, 'blue');
267
697
 
268
698
  // Check and install Tailwind
269
699
  if (!checkPackageInstalled('tailwindcss')) {
270
700
  log('Tailwind CSS not found. Installing...', 'yellow');
271
- if (!installPackage('tailwindcss@^3.4.0')) {
701
+ if (!installPackage(`tailwindcss@${TAILWIND_VERSION}`)) {
272
702
  log('Failed to install Tailwind CSS. Please install manually.', 'red');
273
703
  process.exit(1);
274
704
  }
@@ -290,6 +720,8 @@ function main() {
290
720
  createTailwindConfig();
291
721
  createPostCSSConfig();
292
722
  createCSSFile();
723
+ createTokenFiles();
724
+ injectThemeScript();
293
725
  checkMainFile();
294
726
 
295
727
  log('\n✅ Setup complete!', 'green');
@@ -297,9 +729,12 @@ function main() {
297
729
  log('1. Make sure to import the CSS file in your entry point:', 'yellow');
298
730
  log(" import './index.css'", 'blue');
299
731
  log('2. Start using components:', 'yellow');
300
- log(" import { Button } from 'shru-design-system'", 'blue');
732
+ log(` import { Button, ThemeToggle } from '${PACKAGE_NAME}'`, 'blue');
733
+ log('\n💡 Custom Token Files:', 'blue');
734
+ log(' You can add custom theme files to public/tokens/themes/{category}/', 'yellow');
735
+ log(' Example: public/tokens/themes/color/ocean.json', 'blue');
736
+ log(' The ThemeToggle will automatically discover and use them.', 'blue');
301
737
  log('\n');
302
738
  }
303
739
 
304
740
  main();
305
-