create-lacspace-app 1.14.1 → 1.16.0

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.
Files changed (3) hide show
  1. package/README.md +30 -0
  2. package/dist/index.js +363 -6
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -62,6 +62,23 @@ Edit `lib/site.ts` (your site config) and `app/page.tsx` — you're off.
62
62
 
63
63
  ---
64
64
 
65
+ ## 🧩 Grow your app anytime — `add`
66
+
67
+ Unlike every other `create-*` tool, `create-lacspace-app` doesn't stop after day one. Drop **prebuilt, themed sections** into an existing app whenever you need them:
68
+
69
+ ```bash
70
+ npx create-lacspace-app add pricing faq testimonials
71
+ ```
72
+
73
+ That writes ready-to-use sections into `components/sections/` (and installs the UI kit automatically if it's missing — so it works in **any** Next.js app, not just ones we scaffolded). Then:
74
+
75
+ ```tsx
76
+ import { PricingSection } from "@/components/sections/pricing";
77
+ // …drop <PricingSection /> anywhere in your JSX.
78
+ ```
79
+
80
+ **Available sections:** `hero` · `features` · `pricing` · `faq` · `testimonials` · `team` · `stats` · `timeline` · `gallery` · `logos` · `cta` · `bento`. Run `add` with no arguments to list them.
81
+
65
82
  ## Templates
66
83
 
67
84
  | Key | What you get |
@@ -82,11 +99,24 @@ Every template is Next.js 15 App Router + React 19 + Tailwind v4 — dark, moder
82
99
  | Flag | Meaning |
83
100
  | --- | --- |
84
101
  | `-t, --template <key>` | `personal` · `business` · `ecommerce` · `saas` · `blog` · `docs` · `dashboard` · `restaurant` |
102
+ | `--theme <name\|hex>` | accent gradient — a preset, a `"#hex"`, or `"from,to"` (see below) |
85
103
  | `--pm <npm\|pnpm\|yarn\|bun>` | package manager (default `npm`) |
86
104
  | `--no-install` | skip installing dependencies |
87
105
  | `--no-git` | skip git init |
88
106
  | `-y, --yes` | accept defaults (needs a project name) |
89
107
 
108
+ ### 🎨 Themes
109
+
110
+ Recolour the whole app — gradient, OG images, favicon and PWA theme colour — at scaffold time with `--theme`:
111
+
112
+ ```bash
113
+ npx create-lacspace-app my-app --template saas --theme lacspace # a named preset
114
+ npx create-lacspace-app my-app --theme "#ff6a00" # one colour → auto gradient
115
+ npx create-lacspace-app my-app --theme "#0bb9d9,#7c3aed" # an exact from → to pair
116
+ ```
117
+
118
+ **Presets:** `lacspace` · `violet` · `indigo` · `blue` · `cyan` · `sky` · `teal` · `emerald` · `green` · `lime` · `amber` · `orange` · `red` · `rose` · `pink` · `fuchsia` · `purple` · `slate` · `sunset` · `ocean` · `forest` · `aurora` · `gold`. Give a single hex and the CLI derives a matching second stop for you.
119
+
90
120
  ## Licensing
91
121
 
92
122
  Free under the **[Lacspace Free Licence](https://lacspace.com/licenses/lacspace-free-1.0)** — MIT-equivalent freedoms. Use it in personal and commercial projects at no cost; the apps you generate are entirely yours.
package/dist/index.js CHANGED
@@ -35,11 +35,11 @@ var pkgJson = (ctx) => JSON.stringify({
35
35
  next: "^15.1.0",
36
36
  react: "^19.0.0",
37
37
  "react-dom": "^19.0.0",
38
- "@lacspace/seo": "^1.6.2",
38
+ "@lacspace/seo": "^1.7.0",
39
39
  "@lacspace/headers": "^1.1.2",
40
- "@lacspace/robots": "^1.2.1",
41
- "@lacspace/sitemap": "^1.1.1",
42
- "@lacspace/og": "^1.0.1",
40
+ "@lacspace/robots": "^1.3.0",
41
+ "@lacspace/sitemap": "^1.2.0",
42
+ "@lacspace/og": "^1.1.0",
43
43
  "@lacspace/ui": "^1.0.1",
44
44
  "@lacspace/form": "^1.0.1",
45
45
  "@lacspace/validate": "^1.0.1",
@@ -3450,32 +3450,381 @@ function parseArgs(list) {
3450
3450
  else if (arg === "--no-install") a.install = false;
3451
3451
  else if (arg === "--no-git") a.git = false;
3452
3452
  else if (arg === "--pm") a.pm = next();
3453
+ else if (arg === "--theme" || arg === "--accent") a.theme = next();
3453
3454
  else if (arg === "-h" || arg === "--help") a.help = true;
3454
3455
  else if (arg.startsWith("--template=")) a.template = arg.slice(11);
3456
+ else if (arg.startsWith("--theme=")) a.theme = arg.slice(8);
3457
+ else if (arg.startsWith("--accent=")) a.theme = arg.slice(9);
3455
3458
  else if (!arg.startsWith("-") && !a.name) a.name = arg;
3456
3459
  }
3457
3460
  return a;
3458
3461
  }
3462
+ var THEMES = {
3463
+ lacspace: ["#0bb9d9", "#7c3aed"],
3464
+ violet: ["#7c3aed", "#ec4899"],
3465
+ indigo: ["#6366f1", "#a855f7"],
3466
+ blue: ["#2563eb", "#06b6d4"],
3467
+ cyan: ["#0891b2", "#22d3ee"],
3468
+ sky: ["#0ea5e9", "#38bdf8"],
3469
+ teal: ["#0d9488", "#22c55e"],
3470
+ emerald: ["#10b981", "#14b8a6"],
3471
+ green: ["#16a34a", "#4ade80"],
3472
+ lime: ["#65a30d", "#a3e635"],
3473
+ amber: ["#f59e0b", "#f97316"],
3474
+ orange: ["#ea580c", "#f59e0b"],
3475
+ red: ["#dc2626", "#f97316"],
3476
+ rose: ["#e11d48", "#fb7185"],
3477
+ pink: ["#db2777", "#f472b6"],
3478
+ fuchsia: ["#c026d3", "#f0abfc"],
3479
+ purple: ["#9333ea", "#d946ef"],
3480
+ slate: ["#475569", "#94a3b8"],
3481
+ sunset: ["#f97316", "#db2777"],
3482
+ ocean: ["#0ea5e9", "#6366f1"],
3483
+ forest: ["#16a34a", "#65a30d"],
3484
+ aurora: ["#22d3ee", "#a855f7"],
3485
+ gold: ["#d97706", "#facc15"]
3486
+ };
3487
+ var normHex = (h) => {
3488
+ let s = h.trim().replace(/^#/, "");
3489
+ if (/^[0-9a-fA-F]{3}$/.test(s)) s = s.split("").map((ch) => ch + ch).join("");
3490
+ return /^[0-9a-fA-F]{6}$/.test(s) ? "#" + s.toLowerCase() : null;
3491
+ };
3492
+ var hexToRgb = (h) => {
3493
+ const n = parseInt(h.slice(1), 16);
3494
+ return [n >> 16 & 255, n >> 8 & 255, n & 255];
3495
+ };
3496
+ var rgbToHex = (r, g, b) => "#" + [r, g, b].map((x) => Math.max(0, Math.min(255, Math.round(x))).toString(16).padStart(2, "0")).join("");
3497
+ var rgbToHsl = (r, g, b) => {
3498
+ r /= 255;
3499
+ g /= 255;
3500
+ b /= 255;
3501
+ const max = Math.max(r, g, b), min = Math.min(r, g, b);
3502
+ let h = 0, s = 0;
3503
+ const l = (max + min) / 2;
3504
+ if (max !== min) {
3505
+ const d = max - min;
3506
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
3507
+ h = max === r ? (g - b) / d + (g < b ? 6 : 0) : max === g ? (b - r) / d + 2 : (r - g) / d + 4;
3508
+ h /= 6;
3509
+ }
3510
+ return [h * 360, s, l];
3511
+ };
3512
+ var hslToRgb = (h, s, l) => {
3513
+ h = (h % 360 + 360) % 360 / 360;
3514
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s, p = 2 * l - q;
3515
+ const hue = (t) => {
3516
+ t = (t + 1) % 1;
3517
+ return t < 1 / 6 ? p + (q - p) * 6 * t : t < 1 / 2 ? q : t < 2 / 3 ? p + (q - p) * (2 / 3 - t) * 6 : p;
3518
+ };
3519
+ return [hue(h + 1 / 3) * 255, hue(h) * 255, hue(h - 1 / 3) * 255];
3520
+ };
3521
+ var hexPair = (hex) => {
3522
+ const [h, s, l] = rgbToHsl(...hexToRgb(hex));
3523
+ const [r2, g2, b2] = hslToRgb(h + 28, Math.min(1, s + 0.05), Math.min(0.72, l + 0.1));
3524
+ return [hex, rgbToHex(r2, g2, b2)];
3525
+ };
3526
+ function resolveAccent(theme) {
3527
+ if (!theme) return null;
3528
+ const key = theme.toLowerCase().trim();
3529
+ if (THEMES[key]) return THEMES[key];
3530
+ if (key.includes(",")) {
3531
+ const [a, b] = key.split(",").map((p) => normHex(p));
3532
+ if (a && b) return [a, b];
3533
+ if (a) return hexPair(a);
3534
+ return null;
3535
+ }
3536
+ const one = normHex(key);
3537
+ return one ? hexPair(one) : null;
3538
+ }
3459
3539
  var HELP = `
3460
3540
  ${c("bold", "create-lacspace-app")} \u2014 scaffold a beautiful Next.js app, Lacspace-wired
3461
3541
 
3462
3542
  ${c("bold", "Usage")}
3463
3543
  npm create lacspace-app@latest <name> [options]
3464
3544
  npx create-lacspace-app <name> --template <key>
3545
+ npx create-lacspace-app add <section...> ${c("dim", "# grow an existing app")}
3465
3546
 
3466
3547
  ${c("bold", "Templates")}
3467
3548
  ${TEMPLATES.map((t) => ` ${t.key.padEnd(10)} ${t.description}`).join("\n")}
3468
3549
 
3469
3550
  ${c("bold", "Options")}
3470
3551
  -t, --template <key> Template (${TEMPLATES.map((t) => t.key).join(" | ")})
3552
+ --theme <name|hex> Accent: a preset, a "#hex", or "from,to" (e.g. --theme lacspace)
3471
3553
  --pm <npm|pnpm|yarn|bun> Package manager (default npm)
3472
3554
  --no-install Skip installing dependencies
3473
3555
  --no-git Skip git init
3474
3556
  -y, --yes Accept defaults (needs <name>)
3475
3557
  -h, --help Show this help
3558
+
3559
+ ${c("bold", "Themes")} ${c("dim", "(--theme)")}
3560
+ ${Object.keys(THEMES).join(" \xB7 ")}
3561
+ ${c("dim", 'or a custom colour: --theme "#ff6a00" \xB7 --theme "#0bb9d9,#7c3aed"')}
3562
+
3563
+ ${c("bold", "Add sections to an existing app")}
3564
+ npx create-lacspace-app add pricing faq testimonials
3565
+ ${c("dim", "Drops prebuilt, themed sections into components/sections/ (and the UI kit if missing).")}
3476
3566
  `;
3567
+ var SECTIONS = {
3568
+ hero: `import Link from "next/link";
3569
+ import { Pill } from "@/components/ui";
3570
+
3571
+ export function HeroSection() {
3572
+ return (
3573
+ <section className="mx-auto max-w-4xl px-6 py-28 text-center">
3574
+ <Pill>New</Pill>
3575
+ <h1 className="mt-5 text-5xl font-bold sm:text-6xl">Build something <span className="gradient-text">people love</span></h1>
3576
+ <p className="mx-auto mt-5 max-w-xl text-lg text-muted">A confident headline and one sentence that sells the outcome \u2014 edit me.</p>
3577
+ <div className="mt-8 flex flex-wrap justify-center gap-3">
3578
+ <Link href="/contact" className="rounded-full gradient-bg px-7 py-3 font-semibold text-black transition hover:opacity-90">Get started</Link>
3579
+ <Link href="/about" className="rounded-full border border-hairline px-7 py-3 font-semibold transition hover:bg-surface">Learn more</Link>
3580
+ </div>
3581
+ </section>
3582
+ );
3583
+ }
3584
+ `,
3585
+ features: `import { Section, FeatureCard } from "@/components/ui";
3586
+
3587
+ const ITEMS = [
3588
+ { icon: "\u26A1", title: "Fast", desc: "Server-rendered and instant by default." },
3589
+ { icon: "\u{1F512}", title: "Secure", desc: "Sensible security headers out of the box." },
3590
+ { icon: "\u{1F3A8}", title: "Beautiful", desc: "A polished, themeable design system." },
3591
+ { icon: "\u{1F50E}", title: "SEO-ready", desc: "Metadata, JSON-LD and sitemaps wired." },
3592
+ { icon: "\u{1F4F1}", title: "Responsive", desc: "Looks great on every screen." },
3593
+ { icon: "\u{1F317}", title: "Dark mode", desc: "Light, dark and system \u2014 no flash." },
3594
+ ];
3595
+
3596
+ export function FeaturesSection() {
3597
+ return (
3598
+ <Section eyebrow="Features" title="Everything you need">
3599
+ <div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
3600
+ {ITEMS.map((f) => <FeatureCard key={f.title} icon={f.icon} title={f.title} desc={f.desc} />)}
3601
+ </div>
3602
+ </Section>
3603
+ );
3604
+ }
3605
+ `,
3606
+ pricing: `import { Section, PricingTable } from "@/components/ui";
3607
+
3608
+ export function PricingSection() {
3609
+ return (
3610
+ <Section eyebrow="Pricing" title="Simple, transparent pricing">
3611
+ <PricingTable tiers={[
3612
+ { name: "Starter", price: "$0", period: "mo", features: ["1 project", "Community support", "Basic analytics"] },
3613
+ { name: "Pro", price: "$29", period: "mo", featured: true, features: ["Unlimited projects", "Priority support", "Advanced analytics", "Custom domain"] },
3614
+ { name: "Scale", price: "Custom", features: ["SSO & SAML", "Dedicated support", "SLA & audit logs"] },
3615
+ ]} />
3616
+ </Section>
3617
+ );
3618
+ }
3619
+ `,
3620
+ faq: `import { Section, FAQ } from "@/components/ui";
3621
+
3622
+ export function FaqSection() {
3623
+ return (
3624
+ <Section className="pt-0">
3625
+ <FAQ title="Frequently asked" items={[
3626
+ { q: "Is it production-ready?", a: "Yes \u2014 server-rendered, SEO-optimized and secure by default." },
3627
+ { q: "Can I customize it?", a: "Completely. Colours, fonts and layout are token-driven." },
3628
+ { q: "Does it support dark mode?", a: "Light, dark and system themes with a no-flash script." },
3629
+ { q: "How do I deploy?", a: "One click to any Node host or Vercel." },
3630
+ ]} />
3631
+ </Section>
3632
+ );
3633
+ }
3634
+ `,
3635
+ testimonials: `import { Section, Testimonial } from "@/components/ui";
3636
+
3637
+ const QUOTES = [
3638
+ { quote: "The smoothest launch we've ever had.", author: "Sam Rivera", role: "Product Lead" },
3639
+ { quote: "Fast, polished and easy to build on.", author: "Jordan Ellis", role: "Founder" },
3640
+ { quote: "The attention to detail shows everywhere.", author: "Priya Nair", role: "Design Director" },
3641
+ ];
3642
+
3643
+ export function TestimonialsSection() {
3644
+ return (
3645
+ <Section eyebrow="Loved by teams" title="Don't just take our word for it">
3646
+ <div className="grid gap-6 md:grid-cols-3">
3647
+ {QUOTES.map((t) => <Testimonial key={t.author} quote={t.quote} author={t.author} role={t.role} />)}
3648
+ </div>
3649
+ </Section>
3650
+ );
3651
+ }
3652
+ `,
3653
+ team: `import { Section, TeamGrid } from "@/components/ui";
3654
+
3655
+ export function TeamSection() {
3656
+ return (
3657
+ <Section eyebrow="The team" title="The people behind it">
3658
+ <TeamGrid members={[
3659
+ { name: "Alex Morgan", role: "Founder & CEO", bio: "Sets the vision and keeps us honest." },
3660
+ { name: "Riya Sharma", role: "Head of Design", bio: "Makes every pixel earn its place." },
3661
+ { name: "Chris Doyle", role: "Lead Engineer", bio: "Ships fast without breaking things." },
3662
+ ]} />
3663
+ </Section>
3664
+ );
3665
+ }
3666
+ `,
3667
+ stats: `import { Section, StatBand } from "@/components/ui";
3668
+
3669
+ export function StatsSection() {
3670
+ return (
3671
+ <Section eyebrow="By the numbers" title="Built to perform">
3672
+ <StatBand stats={[
3673
+ { value: "10k+", label: "Monthly visitors" },
3674
+ { value: "99.9%", label: "Uptime" },
3675
+ { value: "4.9\u2605", label: "Average rating" },
3676
+ { value: "<1s", label: "Load time" },
3677
+ ]} />
3678
+ </Section>
3679
+ );
3680
+ }
3681
+ `,
3682
+ timeline: `import { Section, Timeline } from "@/components/ui";
3683
+
3684
+ export function TimelineSection() {
3685
+ return (
3686
+ <Section eyebrow="Our story" title="How we got here">
3687
+ <div className="mx-auto max-w-2xl">
3688
+ <Timeline items={[
3689
+ { date: "Then", title: "The idea", desc: "It started with a simple frustration." },
3690
+ { date: "Next", title: "First release", desc: "We shipped, and people showed up." },
3691
+ { date: "Now", title: "Growing fast", desc: "Thousands of teams, and counting." },
3692
+ ]} />
3693
+ </div>
3694
+ </Section>
3695
+ );
3696
+ }
3697
+ `,
3698
+ gallery: `import { Section, Gallery } from "@/components/ui";
3699
+
3700
+ export function GallerySection() {
3701
+ return (
3702
+ <Section eyebrow="Gallery" title="A look inside">
3703
+ <Gallery items={[
3704
+ { emoji: "\u{1F5BC}\uFE0F", label: "One" }, { emoji: "\u{1F306}", label: "Two" }, { emoji: "\u{1F3A8}", label: "Three" },
3705
+ { emoji: "\u{1F4F8}", label: "Four" }, { emoji: "\u2728", label: "Five" }, { emoji: "\u{1F31F}", label: "Six" },
3706
+ ]} />
3707
+ </Section>
3708
+ );
3709
+ }
3710
+ `,
3711
+ logos: `import { Section, LogoCloud } from "@/components/ui";
3712
+
3713
+ export function LogosSection() {
3714
+ return (
3715
+ <Section className="pt-0">
3716
+ <LogoCloud label="Trusted by teams at" names={["Northwind", "Globex", "Initech", "Umbrella", "Soylent", "Hooli"]} />
3717
+ </Section>
3718
+ );
3719
+ }
3720
+ `,
3721
+ cta: `import { CTABand } from "@/components/ui";
3722
+
3723
+ export function CtaSection() {
3724
+ return <CTABand title="Ready to get started?" subtitle="Join thousands of teams building with us." ctaLabel="Get started" ctaHref="/contact" />;
3725
+ }
3726
+ `,
3727
+ bento: `import { Section, Bento } from "@/components/ui";
3728
+
3729
+ export function BentoSection() {
3730
+ return (
3731
+ <Section eyebrow="One platform" title="Everything, together">
3732
+ <Bento items={[
3733
+ { icon: "\u26A1", title: "Blazing fast", desc: "Instant page loads, everywhere.", className: "sm:col-span-2" },
3734
+ { icon: "\u{1F512}", title: "Secure", desc: "Locked down by default." },
3735
+ { icon: "\u{1F3A8}", title: "Themeable", desc: "Your brand, one token away." },
3736
+ { icon: "\u{1F4CA}", title: "Insightful", desc: "Know what's working.", className: "sm:col-span-2" },
3737
+ ]} />
3738
+ </Section>
3739
+ );
3740
+ }
3741
+ `
3742
+ };
3743
+ function ensureUiKit(root) {
3744
+ const added = [];
3745
+ for (const [rel, content] of Object.entries(uiKitFiles())) {
3746
+ const full = join(root, rel);
3747
+ if (!existsSync(full)) {
3748
+ mkdirSync(dirname(full), { recursive: true });
3749
+ writeFileSync(full, content);
3750
+ added.push(rel);
3751
+ }
3752
+ }
3753
+ return added;
3754
+ }
3755
+ var pascal = (s) => s.split(/[-_]/).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("");
3756
+ function runAdd(rawNames) {
3757
+ stdout.write(`
3758
+ ${c("bold", c("magenta", "\u25C6 create-lacspace-app add"))} ${c("dim", "\u2014 drop prebuilt sections into your app")}
3759
+
3760
+ `);
3761
+ const root = cwd();
3762
+ if (!existsSync(join(root, "package.json"))) {
3763
+ stdout.write(c("red", "\u2717 No package.json here. Run this inside your Next.js project.\n\n"));
3764
+ exit(1);
3765
+ return;
3766
+ }
3767
+ const wanted = rawNames.filter((n) => !n.startsWith("-")).map((n) => n.toLowerCase());
3768
+ if (wanted.length === 0 || rawNames.includes("--help") || rawNames.includes("-h")) {
3769
+ stdout.write(`Usage: ${c("cyan", "npx create-lacspace-app add <section...>")}
3770
+
3771
+ ${c("bold", "Available sections")}
3772
+ `);
3773
+ stdout.write(" " + Object.keys(SECTIONS).map((k) => c("cyan", k)).join(" ") + "\n");
3774
+ stdout.write(`
3775
+ Example: ${c("cyan", "npx create-lacspace-app add pricing faq testimonials")}
3776
+
3777
+ `);
3778
+ return;
3779
+ }
3780
+ const unknown = wanted.filter((n) => !(n in SECTIONS));
3781
+ if (unknown.length) stdout.write(c("yellow", `! Unknown: ${unknown.join(", ")} ${c("dim", "(run with no args to list)")}
3782
+ `));
3783
+ const toAdd = wanted.filter((n) => n in SECTIONS);
3784
+ if (toAdd.length === 0) {
3785
+ exit(1);
3786
+ return;
3787
+ }
3788
+ const kitAdded = ensureUiKit(root);
3789
+ if (kitAdded.length) stdout.write(` ${c("green", "\u2714")} Added the UI kit ${c("dim", "(" + kitAdded.length + " components)")}
3790
+ `);
3791
+ const created = [];
3792
+ for (const name of toAdd) {
3793
+ const rel = `components/sections/${name}.tsx`;
3794
+ const full = join(root, rel);
3795
+ if (existsSync(full)) {
3796
+ stdout.write(c("yellow", ` ~ ${rel} exists \u2014 skipped
3797
+ `));
3798
+ continue;
3799
+ }
3800
+ mkdirSync(dirname(full), { recursive: true });
3801
+ writeFileSync(full, SECTIONS[name]);
3802
+ created.push(name);
3803
+ stdout.write(` ${c("green", "\u2714")} ${c("dim", "+ " + rel)}
3804
+ `);
3805
+ }
3806
+ if (created.length) {
3807
+ stdout.write(`
3808
+ ${c("bold", "Use them")} \u2014 import into any page:
3809
+ `);
3810
+ for (const n of created) stdout.write(` ${c("cyan", `import { ${pascal(n)}Section } from "@/components/sections/${n}";`)}
3811
+ `);
3812
+ stdout.write(`
3813
+ Then drop ${c("cyan", "<" + pascal(created[0]) + "Section />")} into your JSX.
3814
+ `);
3815
+ stdout.write(`
3816
+ ${c("dim", "Sections use the @lacspace UI kit + design tokens \u2014 best inside a create-lacspace-app project.")}
3817
+
3818
+ `);
3819
+ }
3820
+ }
3477
3821
  async function main() {
3478
- const args = parseArgs(argv.slice(2));
3822
+ const raw = argv.slice(2);
3823
+ if (raw[0] === "add") {
3824
+ runAdd(raw.slice(1));
3825
+ return;
3826
+ }
3827
+ const args = parseArgs(raw);
3479
3828
  if (args.help) {
3480
3829
  stdout.write(HELP + "\n");
3481
3830
  return;
@@ -3506,7 +3855,15 @@ ${c("green", "?")} Template ${c("dim", "(1)")}: `)).trim() || "1";
3506
3855
  }
3507
3856
  }
3508
3857
  name = name ?? "my-app";
3509
- const template = TEMPLATES.find((t) => t.key === templateKey) ?? TEMPLATES[0];
3858
+ const base = TEMPLATES.find((t) => t.key === templateKey) ?? TEMPLATES[0];
3859
+ const accent = resolveAccent(args.theme);
3860
+ if (args.theme && !accent) {
3861
+ stdout.write(c("yellow", ` ! Unknown theme "${args.theme}" \u2014 using the ${base.key} default. Try a preset (${Object.keys(THEMES).slice(0, 6).join(", ")}\u2026) or a hex like "#ff6a00".
3862
+ `));
3863
+ }
3864
+ const template = accent ? { ...base, accent } : base;
3865
+ if (accent) stdout.write(` ${c("dim", "theme")} ${c("bold", args.theme)} ${c("dim", `\u2192 ${accent[0]} \u2192 ${accent[1]}`)}
3866
+ `);
3510
3867
  const dir = resolve(cwd(), name);
3511
3868
  const projectName = basename(dir).toLowerCase().replace(/[^a-z0-9-_]/g, "-");
3512
3869
  if (existsSync(dir) && readdirSync(dir).length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-lacspace-app",
3
- "version": "1.14.1",
3
+ "version": "1.16.0",
4
4
  "description": "Scaffold a beautiful, production-ready Next.js app from a Lacspace template — portfolio, business, e-commerce, SaaS, blog, docs, dashboard or restaurant — pre-wired with SEO, security headers, sitemap and robots. Like create-next-app, but you start gorgeous.",
5
5
  "type": "module",
6
6
  "bin": {