@fragments-sdk/cli 0.15.3 → 0.15.5

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.
@@ -298,72 +298,6 @@ function generateDarkTokens(config, format) {
298
298
  }
299
299
  return tokens;
300
300
  }
301
- function generateScssTokens(config) {
302
- const lines = [];
303
- lines.push("// Auto-generated by @fragments-sdk/cli");
304
- lines.push(`// Theme: ${config.name}`);
305
- if (config.version) {
306
- lines.push(`// Version: ${config.version}`);
307
- }
308
- lines.push("");
309
- const colorTokens = generateCategoryTokens(config, "colors", "scss");
310
- if (colorTokens.length > 0) {
311
- lines.push("// Colors");
312
- lines.push(...colorTokens);
313
- lines.push("");
314
- }
315
- const surfaceTokens = generateCategoryTokens(config, "surfaces", "scss");
316
- if (surfaceTokens.length > 0) {
317
- lines.push("// Surfaces");
318
- lines.push(...surfaceTokens);
319
- lines.push("");
320
- }
321
- const textTokens = generateCategoryTokens(config, "text", "scss");
322
- if (textTokens.length > 0) {
323
- lines.push("// Text");
324
- lines.push(...textTokens);
325
- lines.push("");
326
- }
327
- const borderTokens = generateCategoryTokens(config, "borders", "scss");
328
- if (borderTokens.length > 0) {
329
- lines.push("// Borders");
330
- lines.push(...borderTokens);
331
- if (config.borders?.default) {
332
- lines.push(`$fui-border-default: ${config.borders.default} !default;`);
333
- }
334
- lines.push("");
335
- }
336
- const typographyTokens = generateCategoryTokens(config, "typography", "scss");
337
- if (typographyTokens.length > 0) {
338
- lines.push("// Typography");
339
- lines.push(...typographyTokens);
340
- lines.push("");
341
- }
342
- const radiusTokens = generateCategoryTokens(config, "radius", "scss");
343
- if (radiusTokens.length > 0) {
344
- lines.push("// Border Radius");
345
- lines.push(...radiusTokens);
346
- lines.push("");
347
- }
348
- const shadowTokens = generateCategoryTokens(config, "shadows", "scss");
349
- if (shadowTokens.length > 0) {
350
- lines.push("// Shadows");
351
- lines.push(...shadowTokens);
352
- lines.push("");
353
- }
354
- if (config.density) {
355
- lines.push("// Density");
356
- lines.push(`$fui-density: "${config.density}" !default;`);
357
- lines.push("");
358
- }
359
- const darkTokens = generateDarkTokens(config, "scss");
360
- if (darkTokens.length > 0) {
361
- lines.push("// Dark Mode");
362
- lines.push(...darkTokens);
363
- lines.push("");
364
- }
365
- return lines.join("\n");
366
- }
367
301
  function generateCssTokens(config) {
368
302
  const lines = [];
369
303
  lines.push("/* Auto-generated by @fragments-sdk/cli */");
@@ -404,6 +338,66 @@ function generateCssTokens(config) {
404
338
  return lines.join("\n");
405
339
  }
406
340
 
341
+ // src/theme/seed-presets.ts
342
+ var SEED_PRESETS = [
343
+ {
344
+ name: "Stone",
345
+ description: "warm gray",
346
+ seeds: { brand: "#52525b", neutral: "stone", density: "default", radiusStyle: "default" }
347
+ },
348
+ {
349
+ name: "Sand",
350
+ description: "warm beige",
351
+ seeds: { brand: "#8b5e34", neutral: "sand", density: "default", radiusStyle: "default" }
352
+ },
353
+ {
354
+ name: "Ice",
355
+ description: "cool blue",
356
+ seeds: { brand: "#0284c7", neutral: "ice", density: "default", radiusStyle: "default" }
357
+ },
358
+ {
359
+ name: "Earth",
360
+ description: "olive green",
361
+ seeds: { brand: "#517035", neutral: "earth", density: "default", radiusStyle: "default" }
362
+ },
363
+ {
364
+ name: "Fire",
365
+ description: "warm orange",
366
+ seeds: { brand: "#ea580c", neutral: "fire", density: "default", radiusStyle: "default" }
367
+ }
368
+ ];
369
+ var SEED_DEFAULTS = {
370
+ brand: "#18181b",
371
+ neutral: "stone",
372
+ density: "default",
373
+ radiusStyle: "default"
374
+ };
375
+ function generateSeedScss(seeds) {
376
+ const overrides = [];
377
+ if (seeds.brand !== SEED_DEFAULTS.brand) {
378
+ overrides.push(` $fui-brand: ${seeds.brand}`);
379
+ }
380
+ if (seeds.neutral !== SEED_DEFAULTS.neutral) {
381
+ overrides.push(` $fui-neutral: "${seeds.neutral}"`);
382
+ }
383
+ if (seeds.density !== SEED_DEFAULTS.density) {
384
+ overrides.push(` $fui-density: "${seeds.density}"`);
385
+ }
386
+ if (seeds.radiusStyle !== SEED_DEFAULTS.radiusStyle) {
387
+ overrides.push(` $fui-radius-style: "${seeds.radiusStyle}"`);
388
+ }
389
+ if (overrides.length === 0) {
390
+ return `// Fragments UI \u2014 tokens, resets, dark mode
391
+ @use '@fragments-sdk/ui/styles';
392
+ `;
393
+ }
394
+ return `// Fragments UI \u2014 tokens, resets, dark mode
395
+ @use '@fragments-sdk/ui/styles' with (
396
+ ${overrides.join(",\n")}
397
+ );
398
+ `;
399
+ }
400
+
407
401
  // src/commands/create.ts
408
402
  function extractFontFamily(cssFontStack) {
409
403
  const match = cssFontStack.match(/^["']?([^"',]+)["']?/);
@@ -481,7 +475,7 @@ async function resolveTheme(options) {
481
475
  }
482
476
  };
483
477
  }
484
- function generateNextjsLayout(themePath, theme) {
478
+ function generateNextjsLayout(themePath, theme, useSeedPath = false) {
485
479
  const fontName = theme?.typography?.fontSans ? extractFontFamily(theme.typography.fontSans) : null;
486
480
  const fontUrl = fontName ? googleFontsUrl(fontName) : null;
487
481
  const htmlOpen = fontUrl ? ` <html lang="en" suppressHydrationWarning>
@@ -492,9 +486,10 @@ function generateNextjsLayout(themePath, theme) {
492
486
  </head>
493
487
  <body>` : ` <html lang="en" suppressHydrationWarning>
494
488
  <body>`;
489
+ const stylesImport = useSeedPath ? `import '${themePath}';` : `import '@fragments-sdk/ui/styles';
490
+ import '${themePath}';`;
495
491
  return `import type { Metadata } from 'next';
496
- import '@fragments-sdk/ui/styles';
497
- import '${themePath}';
492
+ ${stylesImport}
498
493
  import { Providers } from './providers';
499
494
 
500
495
  export const metadata: Metadata = {
@@ -536,37 +531,251 @@ export function Providers({ children }: { children: ReactNode }) {
536
531
  function generateNextjsPage() {
537
532
  return `'use client';
538
533
 
539
- import { Button, Card, Stack, Text, Input } from '@fragments-sdk/ui';
534
+ import { useState } from 'react';
535
+ import {
536
+ Avatar,
537
+ Badge,
538
+ Button,
539
+ Card,
540
+ Checkbox,
541
+ Input,
542
+ Progress,
543
+ Separator,
544
+ Stack,
545
+ Switch,
546
+ Tabs,
547
+ Text,
548
+ Tooltip,
549
+ } from '@fragments-sdk/ui';
550
+ import styles from './page.module.css';
551
+
552
+ function StatCard({ label, value, change }: { label: string; value: string; change: string }) {
553
+ const isPositive = change.startsWith('+');
554
+ return (
555
+ <Card>
556
+ <Card.Body>
557
+ <Stack gap="xs">
558
+ <Text size="sm" color="secondary">{label}</Text>
559
+ <Text size="2xl" weight="semibold">{value}</Text>
560
+ <Badge variant={isPositive ? 'success' : 'error'} size="sm">{change}</Badge>
561
+ </Stack>
562
+ </Card.Body>
563
+ </Card>
564
+ );
565
+ }
566
+
567
+ function TaskItem({ title, done }: { title: string; done?: boolean }) {
568
+ const [checked, setChecked] = useState(done ?? false);
569
+ return (
570
+ <Stack direction="row" align="center" gap="sm">
571
+ <Checkbox checked={checked} onChange={() => setChecked(!checked)} />
572
+ <Text size="sm" className={checked ? styles.done : undefined}>{title}</Text>
573
+ </Stack>
574
+ );
575
+ }
540
576
 
541
577
  export default function Home() {
578
+ const [notifications, setNotifications] = useState(true);
579
+
542
580
  return (
543
- <main style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
544
- <Card style={{ maxWidth: 480, width: '100%' }}>
545
- <Card.Header>
546
- <Card.Title>Welcome to Fragments</Card.Title>
547
- <Card.Description>Your app is ready. Start building something great.</Card.Description>
548
- </Card.Header>
549
- <Card.Body>
550
- <Stack gap={3}>
551
- <Input placeholder="Enter something..." />
552
- <Stack direction="row" gap={2}>
553
- <Button>Get Started</Button>
554
- <Button variant="secondary">Learn More</Button>
581
+ <main className={styles.page}>
582
+ {/* Hero */}
583
+ <Stack align="center" gap="md" className={styles.hero}>
584
+ <Badge variant="info">Powered by Fragments UI</Badge>
585
+ <Text as="h1" size="2xl" weight="bold">Your app is ready</Text>
586
+ <Text size="lg" color="secondary" className={styles.heroDescription}>
587
+ This page showcases your custom theme and components.
588
+ Edit <code>src/app/page.tsx</code> to start building.
589
+ </Text>
590
+ <Stack direction="row" gap="sm">
591
+ <Button size="lg">Start building</Button>
592
+ <Button size="lg" variant="secondary">Read the docs</Button>
593
+ </Stack>
594
+ </Stack>
595
+
596
+ <Separator />
597
+
598
+ {/* Stats */}
599
+ <div className={styles.statsGrid}>
600
+ <StatCard label="Components" value="66" change="+12 this month" />
601
+ <StatCard label="Design tokens" value="80" change="+5 new" />
602
+ <StatCard label="Accessibility" value="98%" change="+2.4%" />
603
+ </div>
604
+
605
+ {/* Content */}
606
+ <div className={styles.contentGrid}>
607
+ <Card>
608
+ <Card.Header>
609
+ <Card.Title>Getting started</Card.Title>
610
+ <Card.Description>Pick up where you left off</Card.Description>
611
+ </Card.Header>
612
+ <Card.Body>
613
+ <Tabs defaultValue="tasks">
614
+ <Tabs.List>
615
+ <Tabs.Tab value="tasks">Tasks</Tabs.Tab>
616
+ <Tabs.Tab value="progress">Progress</Tabs.Tab>
617
+ </Tabs.List>
618
+ <Tabs.Panel value="tasks">
619
+ <Stack gap="sm" className={styles.tabContent}>
620
+ <TaskItem title="Install Fragments UI" done />
621
+ <TaskItem title="Configure your theme" done />
622
+ <TaskItem title="Build your first page" />
623
+ <TaskItem title="Set up MCP for AI tooling" />
624
+ <TaskItem title="Deploy to production" />
625
+ </Stack>
626
+ </Tabs.Panel>
627
+ <Tabs.Panel value="progress">
628
+ <Stack gap="md" className={styles.tabContent}>
629
+ <Stack gap="xs">
630
+ <Stack direction="row" justify="between">
631
+ <Text size="sm">Setup</Text>
632
+ <Text size="sm" color="secondary">2 of 5</Text>
633
+ </Stack>
634
+ <Progress value={40} />
635
+ </Stack>
636
+ <Text size="sm" color="secondary">
637
+ Complete the remaining tasks to finish setting up your project.
638
+ </Text>
639
+ </Stack>
640
+ </Tabs.Panel>
641
+ </Tabs>
642
+ </Card.Body>
643
+ </Card>
644
+
645
+ <Card>
646
+ <Card.Header>
647
+ <Card.Title>Settings</Card.Title>
648
+ <Card.Description>Manage your preferences</Card.Description>
649
+ </Card.Header>
650
+ <Card.Body>
651
+ <Stack gap="md">
652
+ <Input placeholder="Search settings..." />
653
+ <Stack direction="row" align="center" justify="between">
654
+ <Stack gap="xs">
655
+ <Text size="sm" weight="medium">Notifications</Text>
656
+ <Text size="xs" color="secondary">Receive alerts for updates</Text>
657
+ </Stack>
658
+ <Switch checked={notifications} onChange={() => setNotifications(!notifications)} />
659
+ </Stack>
660
+ <Separator />
661
+ <Stack direction="row" align="center" justify="between">
662
+ <Stack gap="xs">
663
+ <Text size="sm" weight="medium">Theme</Text>
664
+ <Text size="xs" color="secondary">System preference detected</Text>
665
+ </Stack>
666
+ <Badge variant="default" size="sm">Auto</Badge>
667
+ </Stack>
668
+ <Separator />
669
+ <Stack direction="row" gap="sm" align="center">
670
+ <Avatar name="You" size="sm" />
671
+ <Stack gap="xs" className={styles.flex1}>
672
+ <Text size="sm" weight="medium">Your account</Text>
673
+ <Text size="xs" color="secondary">Manage profile and billing</Text>
674
+ </Stack>
675
+ <Tooltip content="Go to account settings">
676
+ <Button variant="ghost" size="sm">Manage</Button>
677
+ </Tooltip>
678
+ </Stack>
555
679
  </Stack>
556
- </Stack>
557
- </Card.Body>
558
- </Card>
680
+ </Card.Body>
681
+ </Card>
682
+ </div>
683
+
684
+ {/* Footer */}
685
+ <Text size="sm" color="secondary" className={styles.footer}>
686
+ Built with{' '}
687
+ <a href="https://usefragments.com" className={styles.link}>Fragments UI</a>
688
+ {' '}&mdash; 66 components, 80 design tokens, accessible by default.
689
+ </Text>
559
690
  </main>
560
691
  );
561
692
  }
562
693
  `;
563
694
  }
564
- function generateViteMain(themePath) {
695
+ function generatePageCssModule() {
696
+ return `.page {
697
+ min-height: 100vh;
698
+ display: flex;
699
+ flex-direction: column;
700
+ align-items: center;
701
+ padding: var(--fui-space-6, 3rem) var(--fui-space-3, 1.5rem);
702
+ gap: var(--fui-space-4, 2rem);
703
+ max-width: 960px;
704
+ margin: 0 auto;
705
+ }
706
+
707
+ .hero {
708
+ text-align: center;
709
+ padding-top: var(--fui-space-4, 2rem);
710
+ padding-bottom: var(--fui-space-2, 1rem);
711
+ }
712
+
713
+ .heroDescription {
714
+ max-width: 480px;
715
+ }
716
+
717
+ .heroDescription code {
718
+ font-size: 0.875em;
719
+ font-family: var(--fui-font-mono, monospace);
720
+ background: var(--fui-bg-secondary);
721
+ padding: 0.125em 0.375em;
722
+ border-radius: var(--fui-radius-sm, 0.25rem);
723
+ }
724
+
725
+ .statsGrid {
726
+ display: grid;
727
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
728
+ gap: var(--fui-space-2, 1rem);
729
+ width: 100%;
730
+ }
731
+
732
+ .contentGrid {
733
+ display: grid;
734
+ grid-template-columns: 1fr 1fr;
735
+ gap: var(--fui-space-3, 1.5rem);
736
+ width: 100%;
737
+ }
738
+
739
+ @media (max-width: 768px) {
740
+ .contentGrid {
741
+ grid-template-columns: 1fr;
742
+ }
743
+ }
744
+
745
+ .tabContent {
746
+ padding-top: var(--fui-space-1, 0.75rem);
747
+ }
748
+
749
+ .done {
750
+ text-decoration: line-through;
751
+ opacity: 0.5;
752
+ }
753
+
754
+ .flex1 {
755
+ flex: 1;
756
+ }
757
+
758
+ .footer {
759
+ padding-top: var(--fui-space-2, 1rem);
760
+ }
761
+
762
+ .link {
763
+ color: inherit;
764
+ text-decoration: underline;
765
+ }
766
+
767
+ .link:hover {
768
+ color: var(--fui-color-accent);
769
+ }
770
+ `;
771
+ }
772
+ function generateViteMain(themePath, useSeedPath = false) {
773
+ const stylesImport = useSeedPath ? `import '${themePath}';` : `import '@fragments-sdk/ui/styles';
774
+ import '${themePath}';`;
565
775
  return `import { StrictMode } from 'react';
566
776
  import { createRoot } from 'react-dom/client';
567
777
  import { ThemeProvider, TooltipProvider, ToastProvider } from '@fragments-sdk/ui';
568
- import '@fragments-sdk/ui/styles';
569
- import '${themePath}';
778
+ ${stylesImport}
570
779
  import App from './App';
571
780
 
572
781
  createRoot(document.getElementById('root')!).render(
@@ -583,32 +792,7 @@ createRoot(document.getElementById('root')!).render(
583
792
  `;
584
793
  }
585
794
  function generateViteApp() {
586
- return `import { Button, Card, Stack, Text, Input } from '@fragments-sdk/ui';
587
-
588
- function App() {
589
- return (
590
- <main style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
591
- <Card style={{ maxWidth: 480, width: '100%' }}>
592
- <Card.Header>
593
- <Card.Title>Welcome to Fragments</Card.Title>
594
- <Card.Description>Your app is ready. Start building something great.</Card.Description>
595
- </Card.Header>
596
- <Card.Body>
597
- <Stack gap={3}>
598
- <Input placeholder="Enter something..." />
599
- <Stack direction="row" gap={2}>
600
- <Button>Get Started</Button>
601
- <Button variant="secondary">Learn More</Button>
602
- </Stack>
603
- </Stack>
604
- </Card.Body>
605
- </Card>
606
- </main>
607
- );
608
- }
609
-
610
- export default App;
611
- `;
795
+ return generateNextjsPage().replace("'use client';\n\n", "").replace("import styles from './page.module.css';", "import styles from './App.module.css';").replace("src/app/page.tsx", "src/App.tsx").replace("export default function Home()", "function App()") + "\nexport default App;\n";
612
796
  }
613
797
  function injectFontIntoViteHtml(projectDir, theme) {
614
798
  const fontName = theme.typography?.fontSans ? extractFontFamily(theme.typography.fontSans) : null;
@@ -660,6 +844,51 @@ async function promptIfMissing(options) {
660
844
  }
661
845
  resolved.template = resolved.template || "nextjs";
662
846
  resolved.packageManager = resolved.packageManager || detectPackageManager();
847
+ if (!resolved.preset && !resolved.theme && !resolved.brand && !resolved.yes) {
848
+ try {
849
+ const { select, input } = await import("@inquirer/prompts");
850
+ const choices = SEED_PRESETS.map((p) => ({
851
+ name: `${p.name} (${p.description})`,
852
+ value: p.name.toLowerCase()
853
+ }));
854
+ choices.push({ name: "Custom (pick color + palette)", value: "custom" });
855
+ const selected = await select({
856
+ message: "Theme:",
857
+ choices,
858
+ default: "stone"
859
+ });
860
+ if (selected === "custom") {
861
+ const brandColor = await input({
862
+ message: "Brand color (hex):",
863
+ default: "#6366f1",
864
+ validate: (v) => /^#[0-9a-fA-F]{6}$/.test(v) || "Enter a valid hex color (e.g., #6366f1)"
865
+ });
866
+ const neutral = await select({
867
+ message: "Neutral palette:",
868
+ choices: [
869
+ { name: "Stone (warm gray)", value: "stone" },
870
+ { name: "Sand (warm beige)", value: "sand" },
871
+ { name: "Ice (cool blue)", value: "ice" },
872
+ { name: "Earth (olive green)", value: "earth" },
873
+ { name: "Fire (warm orange)", value: "fire" }
874
+ ],
875
+ default: "stone"
876
+ });
877
+ resolved.seeds = {
878
+ brand: brandColor,
879
+ neutral,
880
+ density: "default",
881
+ radiusStyle: "default"
882
+ };
883
+ } else {
884
+ const preset = SEED_PRESETS.find((p) => p.name.toLowerCase() === selected);
885
+ if (preset) {
886
+ resolved.seeds = { ...preset.seeds };
887
+ }
888
+ }
889
+ } catch {
890
+ }
891
+ }
663
892
  return resolved;
664
893
  }
665
894
  function scaffoldFramework(name, template, pm) {
@@ -677,26 +906,33 @@ Scaffolding ${template === "nextjs" ? "Next.js" : "Vite + React"} project...
677
906
  }
678
907
  }
679
908
  }
680
- function installDeps(projectDir, pm, scss) {
909
+ function installDeps(projectDir, pm) {
681
910
  console.log(pc.cyan("\nInstalling Fragments UI...\n"));
682
911
  const install = getInstallCommand(pm);
912
+ const devInstall = getDevInstallCommand(pm);
683
913
  execSync(`${install} @fragments-sdk/ui`, { cwd: projectDir, stdio: "inherit" });
684
- if (scss) {
685
- const devInstall = getDevInstallCommand(pm);
686
- execSync(`${devInstall} sass`, { cwd: projectDir, stdio: "inherit" });
687
- }
914
+ execSync(`${devInstall} sass`, { cwd: projectDir, stdio: "inherit" });
688
915
  }
689
- function writeThemeFile(projectDir, theme, scss, template) {
916
+ function writeThemeFile(projectDir, template, source) {
690
917
  const stylesDir = join2(projectDir, "src", "styles");
691
918
  mkdirSync(stylesDir, { recursive: true });
692
- const ext = scss ? "scss" : "css";
693
- const content = scss ? generateScssTokens(theme) : generateCssTokens(theme);
694
- const filePath = join2(stylesDir, `theme.${ext}`);
919
+ let content;
920
+ if (source.kind === "seeds") {
921
+ content = generateSeedScss(source.seeds);
922
+ } else {
923
+ const cssContent = generateCssTokens(source.theme);
924
+ content = `// Fragments UI \u2014 tokens, resets, dark mode
925
+ @use '@fragments-sdk/ui/styles';
926
+
927
+ // Theme overrides (generated from your preset)
928
+ ${cssContent}`;
929
+ }
930
+ const filePath = join2(stylesDir, "theme.scss");
695
931
  writeFileSync(filePath, content, "utf-8");
696
932
  if (template === "nextjs") {
697
- return `../styles/theme.${ext}`;
933
+ return "../styles/theme.scss";
698
934
  }
699
- return `./styles/theme.${ext}`;
935
+ return "./styles/theme.scss";
700
936
  }
701
937
  function addNextTranspilePackages(projectDir) {
702
938
  const configCandidates = ["next.config.ts", "next.config.mjs", "next.config.js"];
@@ -739,19 +975,16 @@ function addNextTranspilePackages(projectDir) {
739
975
  return;
740
976
  }
741
977
  }
742
- function rewriteAppFiles(projectDir, template, themePath, theme) {
978
+ function rewriteAppFiles(projectDir, template, themePath, theme, useSeedPath) {
743
979
  if (template === "nextjs") {
744
980
  const layoutPath = join2(projectDir, "src", "app", "layout.tsx");
745
- writeFileSync(layoutPath, generateNextjsLayout(themePath, theme), "utf-8");
981
+ writeFileSync(layoutPath, generateNextjsLayout(themePath, theme ?? void 0, useSeedPath), "utf-8");
746
982
  const providersPath = join2(projectDir, "src", "app", "providers.tsx");
747
983
  writeFileSync(providersPath, generateNextjsProviders(), "utf-8");
748
984
  const pagePath = join2(projectDir, "src", "app", "page.tsx");
749
985
  writeFileSync(pagePath, generateNextjsPage(), "utf-8");
750
- const moduleCssPath = join2(projectDir, "src", "app", "page.module.css");
751
- try {
752
- unlinkSync(moduleCssPath);
753
- } catch {
754
- }
986
+ const pageCssPath = join2(projectDir, "src", "app", "page.module.css");
987
+ writeFileSync(pageCssPath, generatePageCssModule(), "utf-8");
755
988
  const globalsCssPath = join2(projectDir, "src", "app", "globals.css");
756
989
  try {
757
990
  unlinkSync(globalsCssPath);
@@ -760,16 +993,20 @@ function rewriteAppFiles(projectDir, template, themePath, theme) {
760
993
  addNextTranspilePackages(projectDir);
761
994
  } else {
762
995
  const mainPath = join2(projectDir, "src", "main.tsx");
763
- writeFileSync(mainPath, generateViteMain(themePath), "utf-8");
996
+ writeFileSync(mainPath, generateViteMain(themePath, useSeedPath), "utf-8");
764
997
  const appPath = join2(projectDir, "src", "App.tsx");
765
998
  writeFileSync(appPath, generateViteApp(), "utf-8");
999
+ const appCssModulePath = join2(projectDir, "src", "App.module.css");
1000
+ writeFileSync(appCssModulePath, generatePageCssModule(), "utf-8");
766
1001
  for (const file of ["src/App.css", "src/index.css"]) {
767
1002
  try {
768
1003
  unlinkSync(join2(projectDir, file));
769
1004
  } catch {
770
1005
  }
771
1006
  }
772
- injectFontIntoViteHtml(projectDir, theme);
1007
+ if (theme) {
1008
+ injectFontIntoViteHtml(projectDir, theme);
1009
+ }
773
1010
  }
774
1011
  }
775
1012
  function initGit(projectDir) {
@@ -803,6 +1040,7 @@ ${BRAND.name} Create
803
1040
  const name = resolved.name;
804
1041
  const template = resolved.template;
805
1042
  const pm = resolved.packageManager;
1043
+ const hasSeedPath = !!resolved.seeds;
806
1044
  if (!isValidProjectName(name)) {
807
1045
  return { success: false, error: `Invalid project name: ${name}. Use lowercase letters, numbers, hyphens, dots, underscores.` };
808
1046
  }
@@ -810,18 +1048,21 @@ ${BRAND.name} Create
810
1048
  if (existsSync(projectDir)) {
811
1049
  return { success: false, error: `Directory "${name}" already exists.` };
812
1050
  }
813
- const theme = await resolveTheme(resolved);
814
- if (!theme) {
815
- return { success: false, error: "Invalid theme configuration." };
1051
+ let theme = null;
1052
+ if (!hasSeedPath) {
1053
+ theme = await resolveTheme(resolved);
1054
+ if (!theme) {
1055
+ return { success: false, error: "Invalid theme configuration." };
1056
+ }
816
1057
  }
817
1058
  scaffoldFramework(name, template, pm);
818
1059
  if (!existsSync(projectDir)) {
819
1060
  return { success: false, error: "Framework scaffolding failed \u2014 project directory was not created." };
820
1061
  }
821
- installDeps(projectDir, pm, !!resolved.scss);
822
- const themePath = writeThemeFile(projectDir, theme, !!resolved.scss, template);
823
- rewriteAppFiles(projectDir, template, themePath, theme);
824
- if (resolved.mcp) {
1062
+ installDeps(projectDir, pm);
1063
+ const themePath = hasSeedPath ? writeThemeFile(projectDir, template, { kind: "seeds", seeds: resolved.seeds }) : writeThemeFile(projectDir, template, { kind: "css", theme });
1064
+ rewriteAppFiles(projectDir, template, themePath, theme, hasSeedPath);
1065
+ if (resolved.mcp !== false) {
825
1066
  configureMcp(projectDir);
826
1067
  console.log(pc.dim(" Configured MCP server for AI tooling"));
827
1068
  }
@@ -835,9 +1076,16 @@ ${BRAND.name} Create
835
1076
  console.log(` ${pc.cyan("cd")} ${name}`);
836
1077
  console.log(` ${pc.cyan(run)} dev`);
837
1078
  console.log("");
838
- if (theme.name !== "default") {
1079
+ if (hasSeedPath) {
1080
+ const seeds = resolved.seeds;
1081
+ const presetName = seeds.neutral.charAt(0).toUpperCase() + seeds.neutral.slice(1);
1082
+ console.log(pc.dim(` Theme: ${presetName} palette with ${seeds.brand} brand`));
1083
+ console.log(pc.dim(` Edit src/styles/theme.scss to customize seed values.`));
1084
+ console.log(pc.dim(` Fine-tune: ${pc.cyan("fragments init --configure")}
1085
+ `));
1086
+ } else if (theme && theme.name !== "default") {
839
1087
  console.log(pc.dim(` Theme "${theme.name}" applied with full token set.`));
840
- console.log(pc.dim(` Edit src/styles/theme.${resolved.scss ? "scss" : "css"} to customize.
1088
+ console.log(pc.dim(` Edit src/styles/theme.scss to customize.
841
1089
  `));
842
1090
  }
843
1091
  return { success: true, projectDir };
@@ -847,6 +1095,7 @@ export {
847
1095
  create,
848
1096
  generateNextjsLayout,
849
1097
  generateNextjsPage,
850
- generateNextjsProviders
1098
+ generateNextjsProviders,
1099
+ generatePageCssModule
851
1100
  };
852
- //# sourceMappingURL=create-JVAU3YKN.js.map
1101
+ //# sourceMappingURL=create-AC2PMGBF.js.map