create-kofi-stack 1.0.0 → 1.1.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.
- package/LICENSE +1 -1
- package/dist/index.js +1984 -89
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -97,6 +97,18 @@ async function promptIncludeDocs(defaultValue) {
|
|
|
97
97
|
|
|
98
98
|
// src/prompts/shadcn.ts
|
|
99
99
|
import * as p2 from "@clack/prompts";
|
|
100
|
+
var colorPresets = {
|
|
101
|
+
blue: { primary: "221.2 83.2% 53.3%", secondary: "210 40% 96.1%", accent: "210 40% 96.1%" },
|
|
102
|
+
green: { primary: "142.1 76.2% 36.3%", secondary: "138 76% 97%", accent: "142.1 70.6% 45.3%" },
|
|
103
|
+
orange: { primary: "24.6 95% 53.1%", secondary: "30 80% 96%", accent: "20.5 90.2% 48.2%" },
|
|
104
|
+
red: { primary: "0 72.2% 50.6%", secondary: "0 85% 97%", accent: "0 84.2% 60.2%" },
|
|
105
|
+
violet: { primary: "262.1 83.3% 57.8%", secondary: "260 60% 96%", accent: "263.4 70% 50.4%" },
|
|
106
|
+
yellow: { primary: "47.9 95.8% 53.1%", secondary: "48 100% 96%", accent: "45.4 93.4% 47.5%" },
|
|
107
|
+
teal: { primary: "173 80% 40%", secondary: "166 76% 97%", accent: "172 66% 50.4%" },
|
|
108
|
+
rose: { primary: "346.8 77.2% 49.8%", secondary: "340 80% 97%", accent: "346.8 77.2% 49.8%" },
|
|
109
|
+
indigo: { primary: "238.7 83.5% 66.7%", secondary: "226 70% 97%", accent: "243.4 75.4% 58.6%" },
|
|
110
|
+
cyan: { primary: "189 94% 43%", secondary: "185 96% 97%", accent: "188 94.5% 42.7%" }
|
|
111
|
+
};
|
|
100
112
|
async function promptComponentLibrary(defaultValue) {
|
|
101
113
|
const library = await p2.select({
|
|
102
114
|
message: "Component library:",
|
|
@@ -194,6 +206,96 @@ async function promptIconLibrary(defaultValue) {
|
|
|
194
206
|
}
|
|
195
207
|
return iconLibrary;
|
|
196
208
|
}
|
|
209
|
+
async function promptPrimaryColor(defaultValue) {
|
|
210
|
+
const color = await p2.select({
|
|
211
|
+
message: "Primary brand color:",
|
|
212
|
+
options: [
|
|
213
|
+
{ value: "blue", label: "Blue", hint: "Professional, trustworthy" },
|
|
214
|
+
{ value: "green", label: "Green", hint: "Growth, success" },
|
|
215
|
+
{ value: "violet", label: "Violet", hint: "Creative, premium" },
|
|
216
|
+
{ value: "orange", label: "Orange", hint: "Energetic, friendly" },
|
|
217
|
+
{ value: "red", label: "Red", hint: "Bold, urgent" },
|
|
218
|
+
{ value: "teal", label: "Teal", hint: "Calm, modern" },
|
|
219
|
+
{ value: "rose", label: "Rose", hint: "Warm, approachable" },
|
|
220
|
+
{ value: "indigo", label: "Indigo", hint: "Deep, sophisticated" },
|
|
221
|
+
{ value: "cyan", label: "Cyan", hint: "Fresh, tech-forward" }
|
|
222
|
+
],
|
|
223
|
+
initialValue: defaultValue ?? "blue"
|
|
224
|
+
});
|
|
225
|
+
if (p2.isCancel(color)) {
|
|
226
|
+
throw new Error("Operation cancelled");
|
|
227
|
+
}
|
|
228
|
+
return colorPresets[color].primary;
|
|
229
|
+
}
|
|
230
|
+
async function promptHeadingFont(defaultValue) {
|
|
231
|
+
const font = await p2.select({
|
|
232
|
+
message: "Heading font:",
|
|
233
|
+
options: [
|
|
234
|
+
{ value: "dm-sans", label: "DM Sans", hint: "Modern, geometric" },
|
|
235
|
+
{ value: "inter", label: "Inter", hint: "Clean, versatile" },
|
|
236
|
+
{ value: "geist", label: "Geist", hint: "Vercel's modern font" },
|
|
237
|
+
{ value: "plus-jakarta", label: "Plus Jakarta Sans", hint: "Friendly, readable" },
|
|
238
|
+
{ value: "outfit", label: "Outfit", hint: "Contemporary, rounded" },
|
|
239
|
+
{ value: "space-grotesk", label: "Space Grotesk", hint: "Technical, distinctive" }
|
|
240
|
+
],
|
|
241
|
+
initialValue: defaultValue ?? "dm-sans"
|
|
242
|
+
});
|
|
243
|
+
if (p2.isCancel(font)) {
|
|
244
|
+
throw new Error("Operation cancelled");
|
|
245
|
+
}
|
|
246
|
+
return font;
|
|
247
|
+
}
|
|
248
|
+
async function promptBodyFont(defaultValue) {
|
|
249
|
+
const font = await p2.select({
|
|
250
|
+
message: "Body font:",
|
|
251
|
+
options: [
|
|
252
|
+
{ value: "inter", label: "Inter", hint: "Optimized for screens" },
|
|
253
|
+
{ value: "dm-sans", label: "DM Sans", hint: "Modern, geometric" },
|
|
254
|
+
{ value: "geist", label: "Geist", hint: "Vercel's modern font" },
|
|
255
|
+
{ value: "plus-jakarta", label: "Plus Jakarta Sans", hint: "Friendly, readable" },
|
|
256
|
+
{ value: "outfit", label: "Outfit", hint: "Contemporary, rounded" },
|
|
257
|
+
{ value: "space-grotesk", label: "Space Grotesk", hint: "Technical, distinctive" }
|
|
258
|
+
],
|
|
259
|
+
initialValue: defaultValue ?? "inter"
|
|
260
|
+
});
|
|
261
|
+
if (p2.isCancel(font)) {
|
|
262
|
+
throw new Error("Operation cancelled");
|
|
263
|
+
}
|
|
264
|
+
return font;
|
|
265
|
+
}
|
|
266
|
+
async function promptBorderRadius(defaultValue) {
|
|
267
|
+
const radius = await p2.select({
|
|
268
|
+
message: "Border radius:",
|
|
269
|
+
options: [
|
|
270
|
+
{ value: "none", label: "None", hint: "0px - Sharp edges" },
|
|
271
|
+
{ value: "sm", label: "Small", hint: "4px - Subtle rounding" },
|
|
272
|
+
{ value: "md", label: "Medium", hint: "8px - Balanced (default)" },
|
|
273
|
+
{ value: "lg", label: "Large", hint: "12px - More rounded" },
|
|
274
|
+
{ value: "xl", label: "Extra Large", hint: "16px - Very rounded" },
|
|
275
|
+
{ value: "full", label: "Full", hint: "Pill-shaped buttons" }
|
|
276
|
+
],
|
|
277
|
+
initialValue: defaultValue ?? "md"
|
|
278
|
+
});
|
|
279
|
+
if (p2.isCancel(radius)) {
|
|
280
|
+
throw new Error("Operation cancelled");
|
|
281
|
+
}
|
|
282
|
+
return radius;
|
|
283
|
+
}
|
|
284
|
+
async function promptSpacingScale(defaultValue) {
|
|
285
|
+
const spacing = await p2.select({
|
|
286
|
+
message: "Spacing scale:",
|
|
287
|
+
options: [
|
|
288
|
+
{ value: "compact", label: "Compact", hint: "Tighter spacing, denser UI" },
|
|
289
|
+
{ value: "default", label: "Default", hint: "Standard spacing" },
|
|
290
|
+
{ value: "relaxed", label: "Relaxed", hint: "More breathing room" }
|
|
291
|
+
],
|
|
292
|
+
initialValue: defaultValue ?? "default"
|
|
293
|
+
});
|
|
294
|
+
if (p2.isCancel(spacing)) {
|
|
295
|
+
throw new Error("Operation cancelled");
|
|
296
|
+
}
|
|
297
|
+
return spacing;
|
|
298
|
+
}
|
|
197
299
|
async function promptShadcnConfig(defaults) {
|
|
198
300
|
if (defaults?.componentLibrary && defaults?.styleVariant && defaults?.baseColor && defaults?.themeColor && defaults?.menuAccent && defaults?.iconLibrary) {
|
|
199
301
|
return {
|
|
@@ -213,6 +315,38 @@ async function promptShadcnConfig(defaults) {
|
|
|
213
315
|
const iconLibrary = defaults?.iconLibrary ?? await promptIconLibrary();
|
|
214
316
|
return { componentLibrary, styleVariant, baseColor, themeColor, menuAccent, iconLibrary };
|
|
215
317
|
}
|
|
318
|
+
async function promptDesignSystemConfig(defaults, themeColor) {
|
|
319
|
+
if (defaults?.primaryColor && defaults?.secondaryColor && defaults?.accentColor && defaults?.headingFont && defaults?.bodyFont && defaults?.borderRadius && defaults?.spacingScale) {
|
|
320
|
+
return {
|
|
321
|
+
primaryColor: defaults.primaryColor,
|
|
322
|
+
secondaryColor: defaults.secondaryColor,
|
|
323
|
+
accentColor: defaults.accentColor,
|
|
324
|
+
headingFont: defaults.headingFont,
|
|
325
|
+
bodyFont: defaults.bodyFont,
|
|
326
|
+
borderRadius: defaults.borderRadius,
|
|
327
|
+
spacingScale: defaults.spacingScale
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
const defaultColorKey = themeColor ?? "blue";
|
|
331
|
+
const primaryColor = defaults?.primaryColor ?? await promptPrimaryColor(defaultColorKey);
|
|
332
|
+
const colorKey = Object.entries(colorPresets).find(([_, v]) => v.primary === primaryColor)?.[0] ?? "blue";
|
|
333
|
+
const colors = colorPresets[colorKey];
|
|
334
|
+
const secondaryColor = defaults?.secondaryColor ?? colors.secondary;
|
|
335
|
+
const accentColor = defaults?.accentColor ?? colors.accent;
|
|
336
|
+
const headingFont = defaults?.headingFont ?? await promptHeadingFont();
|
|
337
|
+
const bodyFont = defaults?.bodyFont ?? await promptBodyFont();
|
|
338
|
+
const borderRadius = defaults?.borderRadius ?? await promptBorderRadius();
|
|
339
|
+
const spacingScale = defaults?.spacingScale ?? await promptSpacingScale();
|
|
340
|
+
return {
|
|
341
|
+
primaryColor,
|
|
342
|
+
secondaryColor,
|
|
343
|
+
accentColor,
|
|
344
|
+
headingFont,
|
|
345
|
+
bodyFont,
|
|
346
|
+
borderRadius,
|
|
347
|
+
spacingScale
|
|
348
|
+
};
|
|
349
|
+
}
|
|
216
350
|
|
|
217
351
|
// src/prompts/auth.ts
|
|
218
352
|
import * as p3 from "@clack/prompts";
|
|
@@ -348,6 +482,11 @@ function parseOptions(options) {
|
|
|
348
482
|
themeColor: options.themeColor,
|
|
349
483
|
accent: options.accent,
|
|
350
484
|
iconLibrary: options.iconLibrary,
|
|
485
|
+
primaryColor: options.primaryColor,
|
|
486
|
+
headingFont: options.headingFont,
|
|
487
|
+
bodyFont: options.bodyFont,
|
|
488
|
+
borderRadius: options.borderRadius,
|
|
489
|
+
spacingScale: options.spacingScale,
|
|
351
490
|
authProviders: options.authProviders,
|
|
352
491
|
organizations: options.organizations,
|
|
353
492
|
yes: options.yes,
|
|
@@ -391,6 +530,34 @@ async function runPrompts(projectName, rawOptions) {
|
|
|
391
530
|
menuAccent: options.accent,
|
|
392
531
|
iconLibrary: options.iconLibrary
|
|
393
532
|
});
|
|
533
|
+
let designSystem;
|
|
534
|
+
if (structure === "monorepo") {
|
|
535
|
+
let primaryColorHsl = options.primaryColor;
|
|
536
|
+
if (primaryColorHsl && colorPresets[primaryColorHsl]) {
|
|
537
|
+
primaryColorHsl = colorPresets[primaryColorHsl].primary;
|
|
538
|
+
}
|
|
539
|
+
designSystem = await promptDesignSystemConfig(
|
|
540
|
+
{
|
|
541
|
+
primaryColor: primaryColorHsl,
|
|
542
|
+
headingFont: options.headingFont,
|
|
543
|
+
bodyFont: options.bodyFont,
|
|
544
|
+
borderRadius: options.borderRadius,
|
|
545
|
+
spacingScale: options.spacingScale
|
|
546
|
+
},
|
|
547
|
+
shadcn.themeColor
|
|
548
|
+
);
|
|
549
|
+
} else {
|
|
550
|
+
const colors = colorPresets[shadcn.themeColor] || colorPresets.blue;
|
|
551
|
+
designSystem = {
|
|
552
|
+
primaryColor: colors.primary,
|
|
553
|
+
secondaryColor: colors.secondary,
|
|
554
|
+
accentColor: colors.accent,
|
|
555
|
+
headingFont: "dm-sans",
|
|
556
|
+
bodyFont: "inter",
|
|
557
|
+
borderRadius: "md",
|
|
558
|
+
spacingScale: "default"
|
|
559
|
+
};
|
|
560
|
+
}
|
|
394
561
|
const auth = await promptAuthConfig({
|
|
395
562
|
providers: options.authProviders !== void 0 ? options.authProviders ? options.authProviders.split(",") : [] : void 0,
|
|
396
563
|
organizations: options.organizations
|
|
@@ -408,6 +575,7 @@ async function runPrompts(projectName, rawOptions) {
|
|
|
408
575
|
marketingSite,
|
|
409
576
|
includeDocs,
|
|
410
577
|
shadcn,
|
|
578
|
+
designSystem,
|
|
411
579
|
auth,
|
|
412
580
|
integrations,
|
|
413
581
|
targetDir
|
|
@@ -433,8 +601,19 @@ function getIconLibraryDisplayName(iconLibrary) {
|
|
|
433
601
|
};
|
|
434
602
|
return names[iconLibrary];
|
|
435
603
|
}
|
|
604
|
+
function getFontDisplayName(font) {
|
|
605
|
+
const names = {
|
|
606
|
+
"inter": "Inter",
|
|
607
|
+
"dm-sans": "DM Sans",
|
|
608
|
+
"geist": "Geist",
|
|
609
|
+
"plus-jakarta": "Plus Jakarta Sans",
|
|
610
|
+
"outfit": "Outfit",
|
|
611
|
+
"space-grotesk": "Space Grotesk"
|
|
612
|
+
};
|
|
613
|
+
return names[font];
|
|
614
|
+
}
|
|
436
615
|
function showConfigSummary(config) {
|
|
437
|
-
const apps = ["web"];
|
|
616
|
+
const apps = ["web", "design-system"];
|
|
438
617
|
if (config.structure === "monorepo") {
|
|
439
618
|
if (config.marketingSite !== "none") {
|
|
440
619
|
apps.push(`marketing (${config.marketingSite})`);
|
|
@@ -474,12 +653,13 @@ ${config.structure === "monorepo" ? `${pc.bold("Apps:")} ${apps.join(", ")}
|
|
|
474
653
|
` : ""}${pc.bold("Auth:")} ${authProviders.join(", ")}${config.auth.organizations ? " + Organizations" : ""}
|
|
475
654
|
${pc.bold("UI:")} ${getStyleDisplayName(config)} / ${config.shadcn.themeColor} / ${config.shadcn.menuAccent}
|
|
476
655
|
${pc.bold("Icons:")} ${getIconLibraryDisplayName(config.shadcn.iconLibrary)}
|
|
656
|
+
${pc.bold("Typography:")} ${getFontDisplayName(config.designSystem.headingFont)} / ${getFontDisplayName(config.designSystem.bodyFont)}
|
|
477
657
|
${extras.length > 0 ? `${pc.bold("Extras:")} ${extras.join(", ")}` : ""}`,
|
|
478
658
|
"Scaffolding project:"
|
|
479
659
|
);
|
|
480
660
|
}
|
|
481
661
|
async function showConfirmation(config) {
|
|
482
|
-
const apps = ["web"];
|
|
662
|
+
const apps = ["web", "design-system"];
|
|
483
663
|
if (config.structure === "monorepo") {
|
|
484
664
|
if (config.marketingSite !== "none") {
|
|
485
665
|
apps.push(`marketing (${config.marketingSite})`);
|
|
@@ -519,6 +699,7 @@ ${config.structure === "monorepo" ? `${pc.bold("Apps:")} ${apps.join(", ")}
|
|
|
519
699
|
` : ""}${pc.bold("Auth:")} ${authProviders.join(", ")}${config.auth.organizations ? " + Organizations" : ""}
|
|
520
700
|
${pc.bold("UI:")} ${getStyleDisplayName(config)} / ${config.shadcn.themeColor} / ${config.shadcn.menuAccent}
|
|
521
701
|
${pc.bold("Icons:")} ${getIconLibraryDisplayName(config.shadcn.iconLibrary)}
|
|
702
|
+
${pc.bold("Typography:")} ${getFontDisplayName(config.designSystem.headingFont)} / ${getFontDisplayName(config.designSystem.bodyFont)}
|
|
522
703
|
${extras.length > 0 ? `${pc.bold("Extras:")} ${extras.join(", ")}` : ""}`,
|
|
523
704
|
"Ready to scaffold your project:"
|
|
524
705
|
);
|
|
@@ -532,7 +713,7 @@ ${extras.length > 0 ? `${pc.bold("Extras:")} ${extras.join(", ")}` : ""}`,
|
|
|
532
713
|
}
|
|
533
714
|
|
|
534
715
|
// src/generators/index.ts
|
|
535
|
-
import
|
|
716
|
+
import path19 from "path";
|
|
536
717
|
import * as p6 from "@clack/prompts";
|
|
537
718
|
import pc2 from "picocolors";
|
|
538
719
|
import ora from "ora";
|
|
@@ -1812,7 +1993,7 @@ async function generateConvexPackageJson(convexDir) {
|
|
|
1812
1993
|
types: "./convex/index.ts",
|
|
1813
1994
|
dependencies: {
|
|
1814
1995
|
convex: "^1.17.0",
|
|
1815
|
-
"@convex-dev/better-auth": "^0.
|
|
1996
|
+
"@convex-dev/better-auth": "^0.10.0"
|
|
1816
1997
|
},
|
|
1817
1998
|
devDependencies: {
|
|
1818
1999
|
typescript: "^5.0.0"
|
|
@@ -2929,33 +3110,6 @@ async function generateSharedConfigs(targetDir) {
|
|
|
2929
3110
|
path14.join(targetDir, "packages/config-typescript/package.json"),
|
|
2930
3111
|
tsConfigPackage
|
|
2931
3112
|
);
|
|
2932
|
-
await ensureDir(path14.join(targetDir, "packages/config-tailwind"));
|
|
2933
|
-
const tailwindConfig = `import type { Config } from 'tailwindcss'
|
|
2934
|
-
|
|
2935
|
-
export default {
|
|
2936
|
-
content: [
|
|
2937
|
-
'./src/**/*.{js,ts,jsx,tsx,mdx}',
|
|
2938
|
-
'../../packages/ui/src/**/*.{js,ts,jsx,tsx}',
|
|
2939
|
-
],
|
|
2940
|
-
} satisfies Config
|
|
2941
|
-
`;
|
|
2942
|
-
const tailwindPackage = {
|
|
2943
|
-
name: "@repo/config-tailwind",
|
|
2944
|
-
version: "0.1.0",
|
|
2945
|
-
private: true,
|
|
2946
|
-
main: "tailwind.config.ts",
|
|
2947
|
-
devDependencies: {
|
|
2948
|
-
tailwindcss: "^4.0.0"
|
|
2949
|
-
}
|
|
2950
|
-
};
|
|
2951
|
-
await writeFile(
|
|
2952
|
-
path14.join(targetDir, "packages/config-tailwind/tailwind.config.ts"),
|
|
2953
|
-
tailwindConfig
|
|
2954
|
-
);
|
|
2955
|
-
await writeJSON(
|
|
2956
|
-
path14.join(targetDir, "packages/config-tailwind/package.json"),
|
|
2957
|
-
tailwindPackage
|
|
2958
|
-
);
|
|
2959
3113
|
await ensureDir(path14.join(targetDir, "packages/config-biome"));
|
|
2960
3114
|
const biomeConfigPackage = {
|
|
2961
3115
|
name: "@repo/config-biome",
|
|
@@ -3027,9 +3181,10 @@ async function generateUIPackage(config, targetDir) {
|
|
|
3027
3181
|
include: ["src/**/*"]
|
|
3028
3182
|
};
|
|
3029
3183
|
await writeJSON(path14.join(uiDir, "tsconfig.json"), tsConfig);
|
|
3184
|
+
const style = `${config.shadcn.componentLibrary}-${config.shadcn.styleVariant}`;
|
|
3030
3185
|
const componentsJson = {
|
|
3031
3186
|
$schema: "https://ui.shadcn.com/schema.json",
|
|
3032
|
-
style
|
|
3187
|
+
style,
|
|
3033
3188
|
rsc: false,
|
|
3034
3189
|
tsx: true,
|
|
3035
3190
|
tailwind: {
|
|
@@ -3038,7 +3193,7 @@ async function generateUIPackage(config, targetDir) {
|
|
|
3038
3193
|
baseColor: config.shadcn.baseColor,
|
|
3039
3194
|
cssVariables: true
|
|
3040
3195
|
},
|
|
3041
|
-
iconLibrary:
|
|
3196
|
+
iconLibrary: config.shadcn.iconLibrary,
|
|
3042
3197
|
aliases: {
|
|
3043
3198
|
components: "@/components",
|
|
3044
3199
|
utils: "@/lib/utils",
|
|
@@ -3099,7 +3254,7 @@ async function generatePayloadPackageJson(marketingDir) {
|
|
|
3099
3254
|
"db:seed": "tsx src/seed.ts"
|
|
3100
3255
|
},
|
|
3101
3256
|
dependencies: {
|
|
3102
|
-
next: "^
|
|
3257
|
+
next: "^16.0.0",
|
|
3103
3258
|
react: "^19.0.0",
|
|
3104
3259
|
"react-dom": "^19.0.0",
|
|
3105
3260
|
payload: "^3.0.0",
|
|
@@ -3266,9 +3421,17 @@ export const Media: CollectionConfig = {
|
|
|
3266
3421
|
mediaContent
|
|
3267
3422
|
);
|
|
3268
3423
|
const pagesContent = `import type { CollectionConfig } from 'payload'
|
|
3269
|
-
import {
|
|
3270
|
-
|
|
3271
|
-
|
|
3424
|
+
import {
|
|
3425
|
+
HeroBlock,
|
|
3426
|
+
LogoBannerBlock,
|
|
3427
|
+
FeaturesBlock,
|
|
3428
|
+
BenefitsBlock,
|
|
3429
|
+
PricingBlock,
|
|
3430
|
+
TestimonialsBlock,
|
|
3431
|
+
FAQBlock,
|
|
3432
|
+
ContentBlock,
|
|
3433
|
+
CTABlock,
|
|
3434
|
+
} from '../blocks'
|
|
3272
3435
|
|
|
3273
3436
|
export const Pages: CollectionConfig = {
|
|
3274
3437
|
slug: 'pages',
|
|
@@ -3309,7 +3472,17 @@ export const Pages: CollectionConfig = {
|
|
|
3309
3472
|
{
|
|
3310
3473
|
name: 'layout',
|
|
3311
3474
|
type: 'blocks',
|
|
3312
|
-
blocks: [
|
|
3475
|
+
blocks: [
|
|
3476
|
+
HeroBlock,
|
|
3477
|
+
LogoBannerBlock,
|
|
3478
|
+
FeaturesBlock,
|
|
3479
|
+
BenefitsBlock,
|
|
3480
|
+
PricingBlock,
|
|
3481
|
+
TestimonialsBlock,
|
|
3482
|
+
FAQBlock,
|
|
3483
|
+
ContentBlock,
|
|
3484
|
+
CTABlock,
|
|
3485
|
+
],
|
|
3313
3486
|
},
|
|
3314
3487
|
],
|
|
3315
3488
|
}
|
|
@@ -3522,6 +3695,7 @@ async function generatePayloadBlocks(marketingDir) {
|
|
|
3522
3695
|
|
|
3523
3696
|
export const HeroBlock: Block = {
|
|
3524
3697
|
slug: 'hero',
|
|
3698
|
+
labels: { singular: 'Hero', plural: 'Heroes' },
|
|
3525
3699
|
fields: [
|
|
3526
3700
|
{
|
|
3527
3701
|
name: 'heading',
|
|
@@ -3537,74 +3711,242 @@ export const HeroBlock: Block = {
|
|
|
3537
3711
|
type: 'upload',
|
|
3538
3712
|
relationTo: 'media',
|
|
3539
3713
|
},
|
|
3714
|
+
{
|
|
3715
|
+
name: 'buttons',
|
|
3716
|
+
type: 'array',
|
|
3717
|
+
maxRows: 2,
|
|
3718
|
+
fields: [
|
|
3719
|
+
{ name: 'label', type: 'text', required: true },
|
|
3720
|
+
{ name: 'url', type: 'text', required: true },
|
|
3721
|
+
{
|
|
3722
|
+
name: 'variant',
|
|
3723
|
+
type: 'select',
|
|
3724
|
+
options: [
|
|
3725
|
+
{ label: 'Primary', value: 'primary' },
|
|
3726
|
+
{ label: 'Secondary', value: 'secondary' },
|
|
3727
|
+
],
|
|
3728
|
+
defaultValue: 'primary',
|
|
3729
|
+
},
|
|
3730
|
+
],
|
|
3731
|
+
},
|
|
3732
|
+
],
|
|
3733
|
+
}
|
|
3734
|
+
`;
|
|
3735
|
+
await writeFile(path15.join(marketingDir, "src/blocks/Hero.ts"), heroContent);
|
|
3736
|
+
const logoBannerContent = `import type { Block } from 'payload'
|
|
3737
|
+
|
|
3738
|
+
export const LogoBannerBlock: Block = {
|
|
3739
|
+
slug: 'logoBanner',
|
|
3740
|
+
labels: { singular: 'Logo Banner', plural: 'Logo Banners' },
|
|
3741
|
+
fields: [
|
|
3742
|
+
{ name: 'heading', type: 'text' },
|
|
3743
|
+
{
|
|
3744
|
+
name: 'logos',
|
|
3745
|
+
type: 'array',
|
|
3746
|
+
fields: [
|
|
3747
|
+
{ name: 'name', type: 'text', required: true },
|
|
3748
|
+
{ name: 'logo', type: 'upload', relationTo: 'media' },
|
|
3749
|
+
],
|
|
3750
|
+
},
|
|
3751
|
+
],
|
|
3752
|
+
}
|
|
3753
|
+
`;
|
|
3754
|
+
await writeFile(path15.join(marketingDir, "src/blocks/LogoBanner.ts"), logoBannerContent);
|
|
3755
|
+
const featuresContent = `import type { Block } from 'payload'
|
|
3756
|
+
|
|
3757
|
+
export const FeaturesBlock: Block = {
|
|
3758
|
+
slug: 'features',
|
|
3759
|
+
labels: { singular: 'Features', plural: 'Features' },
|
|
3760
|
+
fields: [
|
|
3761
|
+
{ name: 'heading', type: 'text' },
|
|
3762
|
+
{ name: 'subheading', type: 'textarea' },
|
|
3763
|
+
{
|
|
3764
|
+
name: 'features',
|
|
3765
|
+
type: 'array',
|
|
3766
|
+
fields: [
|
|
3767
|
+
{
|
|
3768
|
+
name: 'icon',
|
|
3769
|
+
type: 'select',
|
|
3770
|
+
options: [
|
|
3771
|
+
{ label: 'Zap', value: 'zap' },
|
|
3772
|
+
{ label: 'Shield', value: 'shield' },
|
|
3773
|
+
{ label: 'Globe', value: 'globe' },
|
|
3774
|
+
{ label: 'Layers', value: 'layers' },
|
|
3775
|
+
{ label: 'Settings', value: 'settings' },
|
|
3776
|
+
{ label: 'Users', value: 'users' },
|
|
3777
|
+
{ label: 'Lock', value: 'lock' },
|
|
3778
|
+
{ label: 'Star', value: 'star' },
|
|
3779
|
+
],
|
|
3780
|
+
},
|
|
3781
|
+
{ name: 'title', type: 'text', required: true },
|
|
3782
|
+
{ name: 'description', type: 'textarea' },
|
|
3783
|
+
],
|
|
3784
|
+
},
|
|
3785
|
+
],
|
|
3786
|
+
}
|
|
3787
|
+
`;
|
|
3788
|
+
await writeFile(path15.join(marketingDir, "src/blocks/Features.ts"), featuresContent);
|
|
3789
|
+
const benefitsContent = `import type { Block } from 'payload'
|
|
3790
|
+
|
|
3791
|
+
export const BenefitsBlock: Block = {
|
|
3792
|
+
slug: 'benefits',
|
|
3793
|
+
labels: { singular: 'Benefits', plural: 'Benefits' },
|
|
3794
|
+
fields: [
|
|
3795
|
+
{ name: 'label', type: 'text' },
|
|
3796
|
+
{ name: 'heading', type: 'text', required: true },
|
|
3797
|
+
{ name: 'description', type: 'textarea' },
|
|
3798
|
+
{ name: 'image', type: 'upload', relationTo: 'media' },
|
|
3799
|
+
{
|
|
3800
|
+
name: 'imagePosition',
|
|
3801
|
+
type: 'select',
|
|
3802
|
+
options: [
|
|
3803
|
+
{ label: 'Left', value: 'left' },
|
|
3804
|
+
{ label: 'Right', value: 'right' },
|
|
3805
|
+
],
|
|
3806
|
+
defaultValue: 'right',
|
|
3807
|
+
},
|
|
3808
|
+
{
|
|
3809
|
+
name: 'benefits',
|
|
3810
|
+
type: 'array',
|
|
3811
|
+
fields: [{ name: 'text', type: 'text', required: true }],
|
|
3812
|
+
},
|
|
3540
3813
|
{
|
|
3541
3814
|
name: 'cta',
|
|
3542
3815
|
type: 'group',
|
|
3543
3816
|
fields: [
|
|
3817
|
+
{ name: 'label', type: 'text' },
|
|
3818
|
+
{ name: 'url', type: 'text' },
|
|
3819
|
+
],
|
|
3820
|
+
},
|
|
3821
|
+
],
|
|
3822
|
+
}
|
|
3823
|
+
`;
|
|
3824
|
+
await writeFile(path15.join(marketingDir, "src/blocks/Benefits.ts"), benefitsContent);
|
|
3825
|
+
const pricingContent = `import type { Block } from 'payload'
|
|
3826
|
+
|
|
3827
|
+
export const PricingBlock: Block = {
|
|
3828
|
+
slug: 'pricing',
|
|
3829
|
+
labels: { singular: 'Pricing', plural: 'Pricing' },
|
|
3830
|
+
fields: [
|
|
3831
|
+
{ name: 'heading', type: 'text' },
|
|
3832
|
+
{ name: 'subheading', type: 'textarea' },
|
|
3833
|
+
{
|
|
3834
|
+
name: 'plans',
|
|
3835
|
+
type: 'array',
|
|
3836
|
+
fields: [
|
|
3837
|
+
{ name: 'name', type: 'text', required: true },
|
|
3838
|
+
{ name: 'price', type: 'text', required: true },
|
|
3839
|
+
{ name: 'period', type: 'text', defaultValue: '/month' },
|
|
3840
|
+
{ name: 'description', type: 'textarea' },
|
|
3841
|
+
{ name: 'featured', type: 'checkbox', defaultValue: false },
|
|
3544
3842
|
{
|
|
3545
|
-
name: '
|
|
3546
|
-
type: '
|
|
3843
|
+
name: 'features',
|
|
3844
|
+
type: 'array',
|
|
3845
|
+
fields: [
|
|
3846
|
+
{ name: 'feature', type: 'text', required: true },
|
|
3847
|
+
{ name: 'included', type: 'checkbox', defaultValue: true },
|
|
3848
|
+
],
|
|
3547
3849
|
},
|
|
3548
3850
|
{
|
|
3549
|
-
name: '
|
|
3550
|
-
type: '
|
|
3851
|
+
name: 'cta',
|
|
3852
|
+
type: 'group',
|
|
3853
|
+
fields: [
|
|
3854
|
+
{ name: 'label', type: 'text' },
|
|
3855
|
+
{ name: 'url', type: 'text' },
|
|
3856
|
+
],
|
|
3551
3857
|
},
|
|
3552
3858
|
],
|
|
3553
3859
|
},
|
|
3554
3860
|
],
|
|
3555
3861
|
}
|
|
3556
3862
|
`;
|
|
3557
|
-
await writeFile(path15.join(marketingDir, "src/blocks/
|
|
3863
|
+
await writeFile(path15.join(marketingDir, "src/blocks/Pricing.ts"), pricingContent);
|
|
3864
|
+
const testimonialsContent = `import type { Block } from 'payload'
|
|
3865
|
+
|
|
3866
|
+
export const TestimonialsBlock: Block = {
|
|
3867
|
+
slug: 'testimonials',
|
|
3868
|
+
labels: { singular: 'Testimonials', plural: 'Testimonials' },
|
|
3869
|
+
fields: [
|
|
3870
|
+
{ name: 'heading', type: 'text' },
|
|
3871
|
+
{ name: 'subheading', type: 'textarea' },
|
|
3872
|
+
{
|
|
3873
|
+
name: 'testimonials',
|
|
3874
|
+
type: 'array',
|
|
3875
|
+
fields: [
|
|
3876
|
+
{ name: 'quote', type: 'textarea', required: true },
|
|
3877
|
+
{ name: 'author', type: 'text', required: true },
|
|
3878
|
+
{ name: 'role', type: 'text' },
|
|
3879
|
+
{ name: 'company', type: 'text' },
|
|
3880
|
+
{ name: 'image', type: 'upload', relationTo: 'media' },
|
|
3881
|
+
],
|
|
3882
|
+
},
|
|
3883
|
+
],
|
|
3884
|
+
}
|
|
3885
|
+
`;
|
|
3886
|
+
await writeFile(path15.join(marketingDir, "src/blocks/Testimonials.ts"), testimonialsContent);
|
|
3887
|
+
const faqContent = `import type { Block } from 'payload'
|
|
3888
|
+
|
|
3889
|
+
export const FAQBlock: Block = {
|
|
3890
|
+
slug: 'faq',
|
|
3891
|
+
labels: { singular: 'FAQ', plural: 'FAQs' },
|
|
3892
|
+
fields: [
|
|
3893
|
+
{ name: 'heading', type: 'text' },
|
|
3894
|
+
{ name: 'subheading', type: 'textarea' },
|
|
3895
|
+
{
|
|
3896
|
+
name: 'items',
|
|
3897
|
+
type: 'array',
|
|
3898
|
+
fields: [
|
|
3899
|
+
{ name: 'question', type: 'text', required: true },
|
|
3900
|
+
{ name: 'answer', type: 'textarea', required: true },
|
|
3901
|
+
],
|
|
3902
|
+
},
|
|
3903
|
+
],
|
|
3904
|
+
}
|
|
3905
|
+
`;
|
|
3906
|
+
await writeFile(path15.join(marketingDir, "src/blocks/FAQ.ts"), faqContent);
|
|
3558
3907
|
const contentContent = `import type { Block } from 'payload'
|
|
3559
3908
|
|
|
3560
3909
|
export const ContentBlock: Block = {
|
|
3561
3910
|
slug: 'content',
|
|
3911
|
+
labels: { singular: 'Content', plural: 'Content' },
|
|
3562
3912
|
fields: [
|
|
3563
|
-
{
|
|
3564
|
-
name: 'content',
|
|
3565
|
-
type: 'richText',
|
|
3566
|
-
},
|
|
3913
|
+
{ name: 'content', type: 'richText' },
|
|
3567
3914
|
],
|
|
3568
3915
|
}
|
|
3569
3916
|
`;
|
|
3570
|
-
await writeFile(
|
|
3571
|
-
path15.join(marketingDir, "src/blocks/Content.ts"),
|
|
3572
|
-
contentContent
|
|
3573
|
-
);
|
|
3917
|
+
await writeFile(path15.join(marketingDir, "src/blocks/Content.ts"), contentContent);
|
|
3574
3918
|
const ctaContent = `import type { Block } from 'payload'
|
|
3575
3919
|
|
|
3576
3920
|
export const CTABlock: Block = {
|
|
3577
3921
|
slug: 'cta',
|
|
3922
|
+
labels: { singular: 'CTA', plural: 'CTAs' },
|
|
3578
3923
|
fields: [
|
|
3924
|
+
{ name: 'heading', type: 'text' },
|
|
3925
|
+
{ name: 'description', type: 'textarea' },
|
|
3579
3926
|
{
|
|
3580
|
-
name: '
|
|
3581
|
-
type: '
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3927
|
+
name: 'style',
|
|
3928
|
+
type: 'select',
|
|
3929
|
+
options: [
|
|
3930
|
+
{ label: 'Light', value: 'light' },
|
|
3931
|
+
{ label: 'Dark', value: 'dark' },
|
|
3932
|
+
{ label: 'Primary', value: 'primary' },
|
|
3933
|
+
],
|
|
3934
|
+
defaultValue: 'dark',
|
|
3586
3935
|
},
|
|
3587
3936
|
{
|
|
3588
3937
|
name: 'buttons',
|
|
3589
3938
|
type: 'array',
|
|
3590
3939
|
maxRows: 2,
|
|
3591
3940
|
fields: [
|
|
3592
|
-
{
|
|
3593
|
-
|
|
3594
|
-
type: 'text',
|
|
3595
|
-
required: true,
|
|
3596
|
-
},
|
|
3597
|
-
{
|
|
3598
|
-
name: 'url',
|
|
3599
|
-
type: 'text',
|
|
3600
|
-
required: true,
|
|
3601
|
-
},
|
|
3941
|
+
{ name: 'label', type: 'text', required: true },
|
|
3942
|
+
{ name: 'url', type: 'text', required: true },
|
|
3602
3943
|
{
|
|
3603
3944
|
name: 'variant',
|
|
3604
3945
|
type: 'select',
|
|
3605
3946
|
options: [
|
|
3606
3947
|
{ label: 'Primary', value: 'primary' },
|
|
3607
3948
|
{ label: 'Secondary', value: 'secondary' },
|
|
3949
|
+
{ label: 'Outline', value: 'outline' },
|
|
3608
3950
|
],
|
|
3609
3951
|
defaultValue: 'primary',
|
|
3610
3952
|
},
|
|
@@ -3615,6 +3957,12 @@ export const CTABlock: Block = {
|
|
|
3615
3957
|
`;
|
|
3616
3958
|
await writeFile(path15.join(marketingDir, "src/blocks/CTA.ts"), ctaContent);
|
|
3617
3959
|
const indexContent = `export { HeroBlock } from './Hero'
|
|
3960
|
+
export { LogoBannerBlock } from './LogoBanner'
|
|
3961
|
+
export { FeaturesBlock } from './Features'
|
|
3962
|
+
export { BenefitsBlock } from './Benefits'
|
|
3963
|
+
export { PricingBlock } from './Pricing'
|
|
3964
|
+
export { TestimonialsBlock } from './Testimonials'
|
|
3965
|
+
export { FAQBlock } from './FAQ'
|
|
3618
3966
|
export { ContentBlock } from './Content'
|
|
3619
3967
|
export { CTABlock } from './CTA'
|
|
3620
3968
|
`;
|
|
@@ -4854,6 +5202,1549 @@ Follow our blog for updates on new features and best practices.
|
|
|
4854
5202
|
);
|
|
4855
5203
|
}
|
|
4856
5204
|
|
|
5205
|
+
// src/generators/design-system.ts
|
|
5206
|
+
import path18 from "path";
|
|
5207
|
+
var fontImports = {
|
|
5208
|
+
inter: {
|
|
5209
|
+
import: "import { Inter } from 'next/font/google'",
|
|
5210
|
+
variable: "--font-inter",
|
|
5211
|
+
config: `const inter = Inter({
|
|
5212
|
+
variable: '--font-inter',
|
|
5213
|
+
subsets: ['latin'],
|
|
5214
|
+
})`
|
|
5215
|
+
},
|
|
5216
|
+
"dm-sans": {
|
|
5217
|
+
import: "import { DM_Sans } from 'next/font/google'",
|
|
5218
|
+
variable: "--font-dm-sans",
|
|
5219
|
+
config: `const dmSans = DM_Sans({
|
|
5220
|
+
variable: '--font-dm-sans',
|
|
5221
|
+
subsets: ['latin'],
|
|
5222
|
+
weight: ['400', '500', '600', '700'],
|
|
5223
|
+
})`
|
|
5224
|
+
},
|
|
5225
|
+
geist: {
|
|
5226
|
+
import: "import { Geist } from 'next/font/google'",
|
|
5227
|
+
variable: "--font-geist",
|
|
5228
|
+
config: `const geist = Geist({
|
|
5229
|
+
variable: '--font-geist',
|
|
5230
|
+
subsets: ['latin'],
|
|
5231
|
+
})`
|
|
5232
|
+
},
|
|
5233
|
+
"plus-jakarta": {
|
|
5234
|
+
import: "import { Plus_Jakarta_Sans } from 'next/font/google'",
|
|
5235
|
+
variable: "--font-plus-jakarta",
|
|
5236
|
+
config: `const plusJakarta = Plus_Jakarta_Sans({
|
|
5237
|
+
variable: '--font-plus-jakarta',
|
|
5238
|
+
subsets: ['latin'],
|
|
5239
|
+
weight: ['400', '500', '600', '700'],
|
|
5240
|
+
})`
|
|
5241
|
+
},
|
|
5242
|
+
outfit: {
|
|
5243
|
+
import: "import { Outfit } from 'next/font/google'",
|
|
5244
|
+
variable: "--font-outfit",
|
|
5245
|
+
config: `const outfit = Outfit({
|
|
5246
|
+
variable: '--font-outfit',
|
|
5247
|
+
subsets: ['latin'],
|
|
5248
|
+
})`
|
|
5249
|
+
},
|
|
5250
|
+
"space-grotesk": {
|
|
5251
|
+
import: "import { Space_Grotesk } from 'next/font/google'",
|
|
5252
|
+
variable: "--font-space-grotesk",
|
|
5253
|
+
config: `const spaceGrotesk = Space_Grotesk({
|
|
5254
|
+
variable: '--font-space-grotesk',
|
|
5255
|
+
subsets: ['latin'],
|
|
5256
|
+
weight: ['400', '500', '600', '700'],
|
|
5257
|
+
})`
|
|
5258
|
+
}
|
|
5259
|
+
};
|
|
5260
|
+
var radiusValues = {
|
|
5261
|
+
none: "0px",
|
|
5262
|
+
sm: "4px",
|
|
5263
|
+
md: "8px",
|
|
5264
|
+
lg: "12px",
|
|
5265
|
+
xl: "16px",
|
|
5266
|
+
full: "9999px"
|
|
5267
|
+
};
|
|
5268
|
+
var spacingMultipliers = {
|
|
5269
|
+
compact: 0.875,
|
|
5270
|
+
default: 1,
|
|
5271
|
+
relaxed: 1.125
|
|
5272
|
+
};
|
|
5273
|
+
async function generateDesignSystemApp(config, targetDir) {
|
|
5274
|
+
const appDir = path18.join(targetDir, "apps/design-system");
|
|
5275
|
+
await ensureDir(path18.join(appDir, "src/app"));
|
|
5276
|
+
await ensureDir(path18.join(appDir, "src/app/colors"));
|
|
5277
|
+
await ensureDir(path18.join(appDir, "src/app/typography"));
|
|
5278
|
+
await ensureDir(path18.join(appDir, "src/app/spacing"));
|
|
5279
|
+
await ensureDir(path18.join(appDir, "src/app/brand"));
|
|
5280
|
+
await ensureDir(path18.join(appDir, "src/app/blocks"));
|
|
5281
|
+
await ensureDir(path18.join(appDir, "src/app/components"));
|
|
5282
|
+
await ensureDir(path18.join(appDir, "src/lib"));
|
|
5283
|
+
await ensureDir(path18.join(appDir, "public/brand"));
|
|
5284
|
+
await generatePackageJson2(config, appDir);
|
|
5285
|
+
await generateTsConfig2(appDir);
|
|
5286
|
+
await generateNextConfig2(appDir);
|
|
5287
|
+
await generatePostCssConfig2(appDir);
|
|
5288
|
+
await generateGlobalsCss2(config, appDir);
|
|
5289
|
+
await generateLayoutTsx(config, appDir);
|
|
5290
|
+
await generateHomePage(config, appDir);
|
|
5291
|
+
await generateColorsPage(config, appDir);
|
|
5292
|
+
await generateTypographyPage(config, appDir);
|
|
5293
|
+
await generateSpacingPage(config, appDir);
|
|
5294
|
+
await generateBrandPage(config, appDir);
|
|
5295
|
+
await generateBlocksPage(appDir);
|
|
5296
|
+
await generateComponentsPage(appDir);
|
|
5297
|
+
await generateUtils(appDir);
|
|
5298
|
+
}
|
|
5299
|
+
async function generatePackageJson2(config, appDir) {
|
|
5300
|
+
const packageJson = {
|
|
5301
|
+
name: "@repo/design-system",
|
|
5302
|
+
version: "0.1.0",
|
|
5303
|
+
private: true,
|
|
5304
|
+
scripts: {
|
|
5305
|
+
dev: "next dev --port 3003",
|
|
5306
|
+
build: "next build",
|
|
5307
|
+
start: "next start",
|
|
5308
|
+
lint: "biome check .",
|
|
5309
|
+
"lint:fix": "biome check --write .",
|
|
5310
|
+
typecheck: "tsc --noEmit"
|
|
5311
|
+
},
|
|
5312
|
+
dependencies: {
|
|
5313
|
+
"@repo/ui": "workspace:*",
|
|
5314
|
+
"class-variance-authority": "^0.7.1",
|
|
5315
|
+
clsx: "^2.1.1",
|
|
5316
|
+
next: "^16.0.0",
|
|
5317
|
+
"next-themes": "^0.4.6",
|
|
5318
|
+
react: "^19.0.0",
|
|
5319
|
+
"react-dom": "^19.0.0",
|
|
5320
|
+
recharts: "^2.15.0",
|
|
5321
|
+
sonner: "^2.0.0",
|
|
5322
|
+
"tailwind-merge": "^3.0.0",
|
|
5323
|
+
"tw-animate-css": "^1.3.0"
|
|
5324
|
+
},
|
|
5325
|
+
devDependencies: {
|
|
5326
|
+
"@repo/config-typescript": "workspace:*",
|
|
5327
|
+
"@tailwindcss/postcss": "^4.0.0",
|
|
5328
|
+
"@types/node": "^20.0.0",
|
|
5329
|
+
"@types/react": "^19.0.0",
|
|
5330
|
+
"@types/react-dom": "^19.0.0",
|
|
5331
|
+
tailwindcss: "^4.0.0",
|
|
5332
|
+
typescript: "^5.0.0"
|
|
5333
|
+
}
|
|
5334
|
+
};
|
|
5335
|
+
await writeJSON(path18.join(appDir, "package.json"), packageJson);
|
|
5336
|
+
}
|
|
5337
|
+
async function generateTsConfig2(appDir) {
|
|
5338
|
+
const tsConfig = {
|
|
5339
|
+
extends: "@repo/config-typescript/nextjs.json",
|
|
5340
|
+
compilerOptions: {
|
|
5341
|
+
jsx: "preserve",
|
|
5342
|
+
paths: {
|
|
5343
|
+
"@/*": ["./src/*"],
|
|
5344
|
+
"@repo/ui": ["../../packages/ui/src"],
|
|
5345
|
+
"@repo/ui/*": ["../../packages/ui/src/*"]
|
|
5346
|
+
}
|
|
5347
|
+
},
|
|
5348
|
+
include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
5349
|
+
exclude: ["node_modules"]
|
|
5350
|
+
};
|
|
5351
|
+
await writeJSON(path18.join(appDir, "tsconfig.json"), tsConfig);
|
|
5352
|
+
}
|
|
5353
|
+
async function generateNextConfig2(appDir) {
|
|
5354
|
+
const content = `import type { NextConfig } from 'next'
|
|
5355
|
+
|
|
5356
|
+
const nextConfig: NextConfig = {
|
|
5357
|
+
transpilePackages: ['@repo/ui'],
|
|
5358
|
+
}
|
|
5359
|
+
|
|
5360
|
+
export default nextConfig
|
|
5361
|
+
`;
|
|
5362
|
+
await writeFile(path18.join(appDir, "next.config.ts"), content);
|
|
5363
|
+
}
|
|
5364
|
+
async function generatePostCssConfig2(appDir) {
|
|
5365
|
+
const content = `export default {
|
|
5366
|
+
plugins: {
|
|
5367
|
+
'@tailwindcss/postcss': {},
|
|
5368
|
+
},
|
|
5369
|
+
}
|
|
5370
|
+
`;
|
|
5371
|
+
await writeFile(path18.join(appDir, "postcss.config.mjs"), content);
|
|
5372
|
+
}
|
|
5373
|
+
async function generateGlobalsCss2(config, appDir) {
|
|
5374
|
+
const { designSystem } = config;
|
|
5375
|
+
const radiusValue = radiusValues[designSystem.borderRadius];
|
|
5376
|
+
const spacingMult = spacingMultipliers[designSystem.spacingScale];
|
|
5377
|
+
const content = `@import "tailwindcss";
|
|
5378
|
+
@import "tw-animate-css";
|
|
5379
|
+
|
|
5380
|
+
@custom-variant dark (&:is(.dark *));
|
|
5381
|
+
|
|
5382
|
+
/* Design System - CSS Variables */
|
|
5383
|
+
|
|
5384
|
+
:root {
|
|
5385
|
+
/* Border Radius */
|
|
5386
|
+
--radius: ${radiusValue};
|
|
5387
|
+
--radius-xs: calc(${radiusValue} * 0.5);
|
|
5388
|
+
--radius-sm: calc(${radiusValue} * 0.75);
|
|
5389
|
+
--radius-md: ${radiusValue};
|
|
5390
|
+
--radius-lg: calc(${radiusValue} * 1.5);
|
|
5391
|
+
--radius-pill: 9999px;
|
|
5392
|
+
|
|
5393
|
+
/* Spacing Scale Multiplier: ${designSystem.spacingScale} (${spacingMult}x) */
|
|
5394
|
+
|
|
5395
|
+
/* Color Tokens - Light Mode */
|
|
5396
|
+
--background: #ffffff;
|
|
5397
|
+
--card: #ffffff;
|
|
5398
|
+
--popover: #ffffff;
|
|
5399
|
+
--muted: #f5f7fa;
|
|
5400
|
+
|
|
5401
|
+
/* Foreground tokens */
|
|
5402
|
+
--foreground: hsl(${designSystem.primaryColor.replace("%", "").split(" ").map((v, i) => i === 2 ? "10%" : v).join(" ")});
|
|
5403
|
+
--card-foreground: hsl(${designSystem.primaryColor.replace("%", "").split(" ").map((v, i) => i === 2 ? "10%" : v).join(" ")});
|
|
5404
|
+
--popover-foreground: hsl(${designSystem.primaryColor.replace("%", "").split(" ").map((v, i) => i === 2 ? "10%" : v).join(" ")});
|
|
5405
|
+
--muted-foreground: hsl(${designSystem.primaryColor.replace("%", "").split(" ").map((v, i) => i === 2 ? "40%" : i === 1 ? "20%" : v).join(" ")});
|
|
5406
|
+
|
|
5407
|
+
/* Brand colors */
|
|
5408
|
+
--primary: hsl(${designSystem.primaryColor});
|
|
5409
|
+
--primary-foreground: #ffffff;
|
|
5410
|
+
--secondary: hsl(${designSystem.secondaryColor});
|
|
5411
|
+
--secondary-foreground: hsl(${designSystem.primaryColor.replace("%", "").split(" ").map((v, i) => i === 2 ? "10%" : v).join(" ")});
|
|
5412
|
+
--accent: hsl(${designSystem.accentColor});
|
|
5413
|
+
--accent-foreground: hsl(${designSystem.primaryColor.replace("%", "").split(" ").map((v, i) => i === 2 ? "10%" : v).join(" ")});
|
|
5414
|
+
|
|
5415
|
+
/* Semantic colors */
|
|
5416
|
+
--success: #22c55e;
|
|
5417
|
+
--warning: #f59e0b;
|
|
5418
|
+
--destructive: #ef4444;
|
|
5419
|
+
--destructive-foreground: #ffffff;
|
|
5420
|
+
|
|
5421
|
+
/* Border and input */
|
|
5422
|
+
--border: #e2e8f0;
|
|
5423
|
+
--input: #e2e8f0;
|
|
5424
|
+
--ring: hsl(${designSystem.primaryColor});
|
|
5425
|
+
|
|
5426
|
+
/* Chart colors */
|
|
5427
|
+
--chart-1: hsl(${designSystem.primaryColor});
|
|
5428
|
+
--chart-2: #22c55e;
|
|
5429
|
+
--chart-3: hsl(${designSystem.accentColor});
|
|
5430
|
+
--chart-4: #ef4444;
|
|
5431
|
+
--chart-5: #6b7280;
|
|
5432
|
+
|
|
5433
|
+
/* Sidebar tokens */
|
|
5434
|
+
--sidebar: #ffffff;
|
|
5435
|
+
--sidebar-foreground: hsl(${designSystem.primaryColor.replace("%", "").split(" ").map((v, i) => i === 2 ? "10%" : v).join(" ")});
|
|
5436
|
+
--sidebar-primary: hsl(${designSystem.primaryColor});
|
|
5437
|
+
--sidebar-primary-foreground: #ffffff;
|
|
5438
|
+
--sidebar-accent: hsla(${designSystem.primaryColor}, 0.1);
|
|
5439
|
+
--sidebar-accent-foreground: hsl(${designSystem.primaryColor.replace("%", "").split(" ").map((v, i) => i === 2 ? "10%" : v).join(" ")});
|
|
5440
|
+
--sidebar-border: #e2e8f0;
|
|
5441
|
+
--sidebar-ring: hsl(${designSystem.primaryColor});
|
|
5442
|
+
}
|
|
5443
|
+
|
|
5444
|
+
.dark {
|
|
5445
|
+
/* Color Tokens - Dark Mode */
|
|
5446
|
+
--background: #0b0d11;
|
|
5447
|
+
--card: #111418;
|
|
5448
|
+
--popover: #111418;
|
|
5449
|
+
--muted: #161a20;
|
|
5450
|
+
|
|
5451
|
+
--foreground: #f5f7fa;
|
|
5452
|
+
--card-foreground: #f5f7fa;
|
|
5453
|
+
--popover-foreground: #f5f7fa;
|
|
5454
|
+
--muted-foreground: #9ba3b0;
|
|
5455
|
+
|
|
5456
|
+
--primary: hsl(${designSystem.primaryColor.replace("%", "").split(" ").map((v, i) => i === 2 ? "60%" : v).join(" ")});
|
|
5457
|
+
--primary-foreground: #ffffff;
|
|
5458
|
+
--secondary: #1e293b;
|
|
5459
|
+
--secondary-foreground: #f8fafc;
|
|
5460
|
+
--accent: #1e293b;
|
|
5461
|
+
--accent-foreground: #f8fafc;
|
|
5462
|
+
|
|
5463
|
+
--success: #4ade80;
|
|
5464
|
+
--warning: #fbbf24;
|
|
5465
|
+
--destructive: #fca5a5;
|
|
5466
|
+
--destructive-foreground: #0f172a;
|
|
5467
|
+
|
|
5468
|
+
--border: rgba(255, 255, 255, 0.1);
|
|
5469
|
+
--input: rgba(255, 255, 255, 0.1);
|
|
5470
|
+
--ring: hsl(${designSystem.primaryColor.replace("%", "").split(" ").map((v, i) => i === 2 ? "60%" : v).join(" ")});
|
|
5471
|
+
|
|
5472
|
+
--chart-1: hsl(${designSystem.primaryColor.replace("%", "").split(" ").map((v, i) => i === 2 ? "60%" : v).join(" ")});
|
|
5473
|
+
--chart-2: #4ade80;
|
|
5474
|
+
--chart-3: hsl(${designSystem.accentColor.replace("%", "").split(" ").map((v, i) => i === 2 ? "60%" : v).join(" ")});
|
|
5475
|
+
--chart-4: #fca5a5;
|
|
5476
|
+
--chart-5: #9ba3b0;
|
|
5477
|
+
|
|
5478
|
+
--sidebar: #0b0d11;
|
|
5479
|
+
--sidebar-foreground: #f5f7fa;
|
|
5480
|
+
--sidebar-primary: hsl(${designSystem.primaryColor.replace("%", "").split(" ").map((v, i) => i === 2 ? "60%" : v).join(" ")});
|
|
5481
|
+
--sidebar-primary-foreground: #ffffff;
|
|
5482
|
+
--sidebar-accent: hsla(${designSystem.primaryColor.replace("%", "").split(" ").map((v, i) => i === 2 ? "60%" : v).join(" ")}, 0.1);
|
|
5483
|
+
--sidebar-accent-foreground: #f5f7fa;
|
|
5484
|
+
--sidebar-border: rgba(255, 255, 255, 0.08);
|
|
5485
|
+
--sidebar-ring: hsl(${designSystem.primaryColor.replace("%", "").split(" ").map((v, i) => i === 2 ? "60%" : v).join(" ")});
|
|
5486
|
+
}
|
|
5487
|
+
|
|
5488
|
+
/* Tailwind Theme Mapping */
|
|
5489
|
+
@theme inline {
|
|
5490
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
5491
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
5492
|
+
--radius-lg: var(--radius);
|
|
5493
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
5494
|
+
|
|
5495
|
+
--color-background: var(--background);
|
|
5496
|
+
--color-foreground: var(--foreground);
|
|
5497
|
+
--color-card: var(--card);
|
|
5498
|
+
--color-card-foreground: var(--card-foreground);
|
|
5499
|
+
--color-popover: var(--popover);
|
|
5500
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
5501
|
+
--color-primary: var(--primary);
|
|
5502
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
5503
|
+
--color-secondary: var(--secondary);
|
|
5504
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
5505
|
+
--color-muted: var(--muted);
|
|
5506
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
5507
|
+
--color-accent: var(--accent);
|
|
5508
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
5509
|
+
--color-destructive: var(--destructive);
|
|
5510
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
5511
|
+
--color-border: var(--border);
|
|
5512
|
+
--color-input: var(--input);
|
|
5513
|
+
--color-ring: var(--ring);
|
|
5514
|
+
--color-success: var(--success);
|
|
5515
|
+
--color-warning: var(--warning);
|
|
5516
|
+
|
|
5517
|
+
--color-chart-1: var(--chart-1);
|
|
5518
|
+
--color-chart-2: var(--chart-2);
|
|
5519
|
+
--color-chart-3: var(--chart-3);
|
|
5520
|
+
--color-chart-4: var(--chart-4);
|
|
5521
|
+
--color-chart-5: var(--chart-5);
|
|
5522
|
+
|
|
5523
|
+
--color-sidebar: var(--sidebar);
|
|
5524
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
5525
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
5526
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
5527
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
5528
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
5529
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
5530
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
5531
|
+
}
|
|
5532
|
+
|
|
5533
|
+
/* Typography */
|
|
5534
|
+
@theme {
|
|
5535
|
+
--font-size-display-hero: 3.5rem;
|
|
5536
|
+
--font-size-display-02: 3rem;
|
|
5537
|
+
--font-size-display-01: 2.5rem;
|
|
5538
|
+
--font-size-h1: 2rem;
|
|
5539
|
+
--font-size-h2: 1.5rem;
|
|
5540
|
+
--font-size-h3: 1.25rem;
|
|
5541
|
+
--font-size-h4: 1.125rem;
|
|
5542
|
+
--font-size-body-lg: 1.125rem;
|
|
5543
|
+
--font-size-body: 1rem;
|
|
5544
|
+
--font-size-body-sm: 0.875rem;
|
|
5545
|
+
|
|
5546
|
+
--line-height-display-hero: 1.1;
|
|
5547
|
+
--line-height-display: 1.2;
|
|
5548
|
+
--line-height-heading: 1.2;
|
|
5549
|
+
--line-height-body: 1.5;
|
|
5550
|
+
--line-height-body-relaxed: 1.75;
|
|
5551
|
+
}
|
|
5552
|
+
|
|
5553
|
+
/* Base styles */
|
|
5554
|
+
html, body {
|
|
5555
|
+
min-height: 100%;
|
|
5556
|
+
overflow-x: hidden;
|
|
5557
|
+
max-width: 100vw;
|
|
5558
|
+
}
|
|
5559
|
+
|
|
5560
|
+
h1, h2, h3, h4, h5, h6 {
|
|
5561
|
+
font-family: var(--font-heading), system-ui, sans-serif;
|
|
5562
|
+
}
|
|
5563
|
+
|
|
5564
|
+
/* Typography Utility Classes */
|
|
5565
|
+
.text-display-hero {
|
|
5566
|
+
font-size: var(--font-size-display-hero);
|
|
5567
|
+
line-height: var(--line-height-display-hero);
|
|
5568
|
+
font-weight: 600;
|
|
5569
|
+
}
|
|
5570
|
+
|
|
5571
|
+
.text-display-02 {
|
|
5572
|
+
font-size: var(--font-size-display-02);
|
|
5573
|
+
line-height: var(--line-height-display);
|
|
5574
|
+
font-weight: 600;
|
|
5575
|
+
}
|
|
5576
|
+
|
|
5577
|
+
.text-display-01 {
|
|
5578
|
+
font-size: var(--font-size-display-01);
|
|
5579
|
+
line-height: var(--line-height-display);
|
|
5580
|
+
font-weight: 600;
|
|
5581
|
+
}
|
|
5582
|
+
|
|
5583
|
+
.text-h1 {
|
|
5584
|
+
font-size: var(--font-size-h1);
|
|
5585
|
+
line-height: var(--line-height-heading);
|
|
5586
|
+
font-weight: 600;
|
|
5587
|
+
}
|
|
5588
|
+
|
|
5589
|
+
.text-h2 {
|
|
5590
|
+
font-size: var(--font-size-h2);
|
|
5591
|
+
line-height: var(--line-height-heading);
|
|
5592
|
+
font-weight: 600;
|
|
5593
|
+
}
|
|
5594
|
+
|
|
5595
|
+
.text-h3 {
|
|
5596
|
+
font-size: var(--font-size-h3);
|
|
5597
|
+
line-height: var(--line-height-heading);
|
|
5598
|
+
font-weight: 500;
|
|
5599
|
+
}
|
|
5600
|
+
|
|
5601
|
+
.text-h4 {
|
|
5602
|
+
font-size: var(--font-size-h4);
|
|
5603
|
+
line-height: var(--line-height-heading);
|
|
5604
|
+
font-weight: 500;
|
|
5605
|
+
}
|
|
5606
|
+
|
|
5607
|
+
.text-body-lg {
|
|
5608
|
+
font-size: var(--font-size-body-lg);
|
|
5609
|
+
line-height: var(--line-height-body-relaxed);
|
|
5610
|
+
}
|
|
5611
|
+
|
|
5612
|
+
.text-body {
|
|
5613
|
+
font-size: var(--font-size-body);
|
|
5614
|
+
line-height: var(--line-height-body);
|
|
5615
|
+
}
|
|
5616
|
+
|
|
5617
|
+
.text-body-sm {
|
|
5618
|
+
font-size: var(--font-size-body-sm);
|
|
5619
|
+
line-height: var(--line-height-body);
|
|
5620
|
+
}
|
|
5621
|
+
|
|
5622
|
+
@layer base {
|
|
5623
|
+
* {
|
|
5624
|
+
@apply border-border outline-ring/50;
|
|
5625
|
+
}
|
|
5626
|
+
body {
|
|
5627
|
+
@apply bg-background text-foreground;
|
|
5628
|
+
}
|
|
5629
|
+
}
|
|
5630
|
+
`;
|
|
5631
|
+
await writeFile(path18.join(appDir, "src/app/globals.css"), content);
|
|
5632
|
+
}
|
|
5633
|
+
async function generateLayoutTsx(config, appDir) {
|
|
5634
|
+
const { designSystem } = config;
|
|
5635
|
+
const headingFont = fontImports[designSystem.headingFont];
|
|
5636
|
+
const bodyFont = fontImports[designSystem.bodyFont];
|
|
5637
|
+
const imports = [];
|
|
5638
|
+
const fontConfigs = [];
|
|
5639
|
+
const fontVars = [];
|
|
5640
|
+
if (headingFont) {
|
|
5641
|
+
imports.push(headingFont.import);
|
|
5642
|
+
fontConfigs.push(headingFont.config.replace(/const \w+/, "const headingFont"));
|
|
5643
|
+
fontVars.push("${headingFont.variable}");
|
|
5644
|
+
}
|
|
5645
|
+
if (bodyFont && bodyFont.variable !== headingFont?.variable) {
|
|
5646
|
+
imports.push(bodyFont.import);
|
|
5647
|
+
fontConfigs.push(bodyFont.config.replace(/const \w+/, "const bodyFont"));
|
|
5648
|
+
fontVars.push("${bodyFont.variable}");
|
|
5649
|
+
}
|
|
5650
|
+
const uniqueImports = [...new Set(imports)];
|
|
5651
|
+
const content = `import type { Metadata } from 'next'
|
|
5652
|
+
${uniqueImports.join("\n")}
|
|
5653
|
+
import { ThemeProvider } from 'next-themes'
|
|
5654
|
+
import './globals.css'
|
|
5655
|
+
|
|
5656
|
+
${fontConfigs.join("\n\n")}
|
|
5657
|
+
|
|
5658
|
+
export const metadata: Metadata = {
|
|
5659
|
+
title: '${config.name} Design System',
|
|
5660
|
+
description: 'Design system documentation for ${config.name}',
|
|
5661
|
+
}
|
|
5662
|
+
|
|
5663
|
+
export default function RootLayout({
|
|
5664
|
+
children,
|
|
5665
|
+
}: Readonly<{
|
|
5666
|
+
children: React.ReactNode
|
|
5667
|
+
}>) {
|
|
5668
|
+
return (
|
|
5669
|
+
<html lang="en" suppressHydrationWarning>
|
|
5670
|
+
<body
|
|
5671
|
+
className={\`\${headingFont.variable}${bodyFont && bodyFont.variable !== headingFont?.variable ? " ${bodyFont.variable}" : ""} font-sans antialiased bg-background text-foreground\`}
|
|
5672
|
+
style={{ '--font-heading': 'var(${headingFont.variable})' } as React.CSSProperties}
|
|
5673
|
+
>
|
|
5674
|
+
<ThemeProvider attribute="class" defaultTheme="light" enableSystem>
|
|
5675
|
+
<div className="min-h-screen flex">
|
|
5676
|
+
<Sidebar projectName="${config.name}" />
|
|
5677
|
+
<main className="flex-1 overflow-auto">{children}</main>
|
|
5678
|
+
</div>
|
|
5679
|
+
</ThemeProvider>
|
|
5680
|
+
</body>
|
|
5681
|
+
</html>
|
|
5682
|
+
)
|
|
5683
|
+
}
|
|
5684
|
+
|
|
5685
|
+
const components = [
|
|
5686
|
+
'Accordion', 'Alert', 'Alert Dialog', 'Aspect Ratio', 'Avatar', 'Badge',
|
|
5687
|
+
'Breadcrumb', 'Button', 'Calendar', 'Card', 'Carousel', 'Chart', 'Checkbox',
|
|
5688
|
+
'Collapsible', 'Command', 'Context Menu', 'Dialog', 'Drawer', 'Dropdown Menu',
|
|
5689
|
+
'Form', 'Hover Card', 'Input', 'Input OTP', 'Label', 'Menubar', 'Navigation Menu',
|
|
5690
|
+
'Pagination', 'Popover', 'Progress', 'Radio Group', 'Resizable', 'Scroll Area',
|
|
5691
|
+
'Select', 'Separator', 'Sheet', 'Sidebar', 'Skeleton', 'Slider', 'Sonner',
|
|
5692
|
+
'Switch', 'Table', 'Tabs', 'Textarea', 'Toast', 'Toggle', 'Toggle Group', 'Tooltip',
|
|
5693
|
+
]
|
|
5694
|
+
|
|
5695
|
+
function Sidebar({ projectName }: { projectName: string }) {
|
|
5696
|
+
const foundationItems = [
|
|
5697
|
+
{ href: '/', label: 'Overview', icon: 'home' },
|
|
5698
|
+
{ href: '/colors', label: 'Colors', icon: 'palette' },
|
|
5699
|
+
{ href: '/typography', label: 'Typography', icon: 'type' },
|
|
5700
|
+
{ href: '/spacing', label: 'Spacing', icon: 'ruler' },
|
|
5701
|
+
{ href: '/brand', label: 'Brand', icon: 'badge' },
|
|
5702
|
+
]
|
|
5703
|
+
|
|
5704
|
+
return (
|
|
5705
|
+
<aside className="w-64 border-r border-border bg-card hidden md:flex md:flex-col h-screen sticky top-0">
|
|
5706
|
+
<div className="p-6">
|
|
5707
|
+
<a href="/" className="flex items-center gap-3">
|
|
5708
|
+
<div className="h-8 w-8 rounded-lg bg-primary flex items-center justify-center text-primary-foreground font-semibold">
|
|
5709
|
+
{projectName.charAt(0).toUpperCase()}
|
|
5710
|
+
</div>
|
|
5711
|
+
<span className="text-h4 font-semibold text-foreground">
|
|
5712
|
+
{projectName} Design
|
|
5713
|
+
</span>
|
|
5714
|
+
</a>
|
|
5715
|
+
</div>
|
|
5716
|
+
<nav className="flex-1 overflow-y-auto px-3 pb-6">
|
|
5717
|
+
<div className="mb-4">
|
|
5718
|
+
<p className="px-3 mb-2 text-xs font-semibold text-muted-foreground uppercase tracking-wider">
|
|
5719
|
+
Foundation
|
|
5720
|
+
</p>
|
|
5721
|
+
<div className="space-y-0.5">
|
|
5722
|
+
{foundationItems.map((item) => (
|
|
5723
|
+
<a
|
|
5724
|
+
key={item.href}
|
|
5725
|
+
href={item.href}
|
|
5726
|
+
className="flex items-center gap-3 px-3 py-2 rounded-md text-sm text-muted-foreground hover:text-foreground hover:bg-muted transition-colors"
|
|
5727
|
+
>
|
|
5728
|
+
<NavIcon name={item.icon} />
|
|
5729
|
+
<span>{item.label}</span>
|
|
5730
|
+
</a>
|
|
5731
|
+
))}
|
|
5732
|
+
</div>
|
|
5733
|
+
</div>
|
|
5734
|
+
|
|
5735
|
+
<div className="mb-4">
|
|
5736
|
+
<p className="px-3 mb-2 text-xs font-semibold text-muted-foreground uppercase tracking-wider">
|
|
5737
|
+
Blocks
|
|
5738
|
+
</p>
|
|
5739
|
+
<div className="space-y-0.5">
|
|
5740
|
+
<a
|
|
5741
|
+
href="/blocks"
|
|
5742
|
+
className="flex items-center gap-3 px-3 py-2 rounded-md text-sm text-muted-foreground hover:text-foreground hover:bg-muted transition-colors"
|
|
5743
|
+
>
|
|
5744
|
+
<NavIcon name="grid" />
|
|
5745
|
+
<span>All Blocks</span>
|
|
5746
|
+
</a>
|
|
5747
|
+
</div>
|
|
5748
|
+
</div>
|
|
5749
|
+
|
|
5750
|
+
<div>
|
|
5751
|
+
<p className="px-3 mb-2 text-xs font-semibold text-muted-foreground uppercase tracking-wider">
|
|
5752
|
+
Components
|
|
5753
|
+
</p>
|
|
5754
|
+
<div className="space-y-0.5">
|
|
5755
|
+
{components.map((component) => {
|
|
5756
|
+
const slug = component.toLowerCase().replace(/\\s+/g, '-')
|
|
5757
|
+
return (
|
|
5758
|
+
<a
|
|
5759
|
+
key={slug}
|
|
5760
|
+
href={\`/components/\${slug}\`}
|
|
5761
|
+
className="block px-3 py-1.5 rounded-md text-sm text-muted-foreground hover:text-foreground hover:bg-muted transition-colors"
|
|
5762
|
+
>
|
|
5763
|
+
{component}
|
|
5764
|
+
</a>
|
|
5765
|
+
)
|
|
5766
|
+
})}
|
|
5767
|
+
</div>
|
|
5768
|
+
</div>
|
|
5769
|
+
</nav>
|
|
5770
|
+
<div className="p-4 border-t border-border">
|
|
5771
|
+
<ThemeToggle />
|
|
5772
|
+
</div>
|
|
5773
|
+
</aside>
|
|
5774
|
+
)
|
|
5775
|
+
}
|
|
5776
|
+
|
|
5777
|
+
function NavIcon({ name }: { name: string }) {
|
|
5778
|
+
const icons: Record<string, React.ReactNode> = {
|
|
5779
|
+
home: (
|
|
5780
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
5781
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
|
5782
|
+
</svg>
|
|
5783
|
+
),
|
|
5784
|
+
palette: (
|
|
5785
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
5786
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01" />
|
|
5787
|
+
</svg>
|
|
5788
|
+
),
|
|
5789
|
+
type: (
|
|
5790
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
5791
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 6h16M4 12h16m-7 6h7" />
|
|
5792
|
+
</svg>
|
|
5793
|
+
),
|
|
5794
|
+
ruler: (
|
|
5795
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
5796
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
|
5797
|
+
</svg>
|
|
5798
|
+
),
|
|
5799
|
+
badge: (
|
|
5800
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
5801
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z" />
|
|
5802
|
+
</svg>
|
|
5803
|
+
),
|
|
5804
|
+
grid: (
|
|
5805
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
5806
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zm10 0a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zm10 0a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z" />
|
|
5807
|
+
</svg>
|
|
5808
|
+
),
|
|
5809
|
+
}
|
|
5810
|
+
return icons[name] || null
|
|
5811
|
+
}
|
|
5812
|
+
|
|
5813
|
+
function ThemeToggle() {
|
|
5814
|
+
return (
|
|
5815
|
+
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
5816
|
+
<button className="p-2 rounded-lg hover:bg-muted transition-colors" title="Light mode">
|
|
5817
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
5818
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
|
|
5819
|
+
</svg>
|
|
5820
|
+
</button>
|
|
5821
|
+
<button className="p-2 rounded-lg hover:bg-muted transition-colors" title="Dark mode">
|
|
5822
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
5823
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
|
|
5824
|
+
</svg>
|
|
5825
|
+
</button>
|
|
5826
|
+
</div>
|
|
5827
|
+
)
|
|
5828
|
+
}
|
|
5829
|
+
`;
|
|
5830
|
+
await writeFile(path18.join(appDir, "src/app/layout.tsx"), content);
|
|
5831
|
+
}
|
|
5832
|
+
async function generateHomePage(config, appDir) {
|
|
5833
|
+
const content = `export default function DesignSystemHome() {
|
|
5834
|
+
return (
|
|
5835
|
+
<div className="p-8 max-w-4xl">
|
|
5836
|
+
<header className="mb-12">
|
|
5837
|
+
<h1 className="text-display-01 text-foreground mb-4">
|
|
5838
|
+
${config.name} Design System
|
|
5839
|
+
</h1>
|
|
5840
|
+
<p className="text-body-lg text-muted-foreground max-w-2xl">
|
|
5841
|
+
A comprehensive design system for building consistent, accessible, and
|
|
5842
|
+
beautiful interfaces across all ${config.name} products.
|
|
5843
|
+
</p>
|
|
5844
|
+
</header>
|
|
5845
|
+
|
|
5846
|
+
<section className="mb-12">
|
|
5847
|
+
<h2 className="text-h2 text-foreground mb-6">Quick Start</h2>
|
|
5848
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
5849
|
+
<QuickLink
|
|
5850
|
+
href="/colors"
|
|
5851
|
+
title="Colors"
|
|
5852
|
+
description="Brand colors, semantic tokens, and usage guidelines"
|
|
5853
|
+
icon="palette"
|
|
5854
|
+
/>
|
|
5855
|
+
<QuickLink
|
|
5856
|
+
href="/typography"
|
|
5857
|
+
title="Typography"
|
|
5858
|
+
description="Font families, type scale, and text styles"
|
|
5859
|
+
icon="type"
|
|
5860
|
+
/>
|
|
5861
|
+
<QuickLink
|
|
5862
|
+
href="/spacing"
|
|
5863
|
+
title="Spacing"
|
|
5864
|
+
description="Spacing scale, layout guidelines, and border radius"
|
|
5865
|
+
icon="ruler"
|
|
5866
|
+
/>
|
|
5867
|
+
<QuickLink
|
|
5868
|
+
href="/components"
|
|
5869
|
+
title="Components"
|
|
5870
|
+
description="UI components with examples and code"
|
|
5871
|
+
icon="box"
|
|
5872
|
+
/>
|
|
5873
|
+
</div>
|
|
5874
|
+
</section>
|
|
5875
|
+
|
|
5876
|
+
<section className="mb-12">
|
|
5877
|
+
<h2 className="text-h2 text-foreground mb-6">Design Principles</h2>
|
|
5878
|
+
<div className="space-y-4">
|
|
5879
|
+
<Principle
|
|
5880
|
+
title="Consistency"
|
|
5881
|
+
description="Use the defined design tokens consistently across all interfaces for a cohesive experience."
|
|
5882
|
+
/>
|
|
5883
|
+
<Principle
|
|
5884
|
+
title="Accessibility"
|
|
5885
|
+
description="All color combinations meet WCAG AA contrast requirements. Design for everyone."
|
|
5886
|
+
/>
|
|
5887
|
+
<Principle
|
|
5888
|
+
title="Simplicity"
|
|
5889
|
+
description="Keep interfaces clean and focused. Every element should serve a purpose."
|
|
5890
|
+
/>
|
|
5891
|
+
<Principle
|
|
5892
|
+
title="Flexibility"
|
|
5893
|
+
description="Components are designed to be adaptable while maintaining visual harmony."
|
|
5894
|
+
/>
|
|
5895
|
+
</div>
|
|
5896
|
+
</section>
|
|
5897
|
+
|
|
5898
|
+
<section>
|
|
5899
|
+
<h2 className="text-h2 text-foreground mb-6">Resources</h2>
|
|
5900
|
+
<div className="flex flex-wrap gap-4">
|
|
5901
|
+
<a
|
|
5902
|
+
href="/brand"
|
|
5903
|
+
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg border border-border hover:bg-muted transition-colors text-sm"
|
|
5904
|
+
>
|
|
5905
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
5906
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
|
5907
|
+
</svg>
|
|
5908
|
+
Brand Assets
|
|
5909
|
+
</a>
|
|
5910
|
+
<a
|
|
5911
|
+
href="/blocks"
|
|
5912
|
+
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg border border-border hover:bg-muted transition-colors text-sm"
|
|
5913
|
+
>
|
|
5914
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
5915
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zm10 0a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6z" />
|
|
5916
|
+
</svg>
|
|
5917
|
+
UI Blocks
|
|
5918
|
+
</a>
|
|
5919
|
+
</div>
|
|
5920
|
+
</section>
|
|
5921
|
+
</div>
|
|
5922
|
+
)
|
|
5923
|
+
}
|
|
5924
|
+
|
|
5925
|
+
function QuickLink({
|
|
5926
|
+
href,
|
|
5927
|
+
title,
|
|
5928
|
+
description,
|
|
5929
|
+
icon,
|
|
5930
|
+
}: {
|
|
5931
|
+
href: string
|
|
5932
|
+
title: string
|
|
5933
|
+
description: string
|
|
5934
|
+
icon: string
|
|
5935
|
+
}) {
|
|
5936
|
+
const icons: Record<string, React.ReactNode> = {
|
|
5937
|
+
palette: (
|
|
5938
|
+
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
5939
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01" />
|
|
5940
|
+
</svg>
|
|
5941
|
+
),
|
|
5942
|
+
type: (
|
|
5943
|
+
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
5944
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 6h16M4 12h16m-7 6h7" />
|
|
5945
|
+
</svg>
|
|
5946
|
+
),
|
|
5947
|
+
ruler: (
|
|
5948
|
+
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
5949
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
|
5950
|
+
</svg>
|
|
5951
|
+
),
|
|
5952
|
+
box: (
|
|
5953
|
+
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
5954
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
|
|
5955
|
+
</svg>
|
|
5956
|
+
),
|
|
5957
|
+
}
|
|
5958
|
+
|
|
5959
|
+
return (
|
|
5960
|
+
<a
|
|
5961
|
+
href={href}
|
|
5962
|
+
className="group block p-6 rounded-xl border border-border hover:border-primary/30 hover:bg-muted/50 transition-all"
|
|
5963
|
+
>
|
|
5964
|
+
<div className="flex items-start gap-4">
|
|
5965
|
+
<div className="p-2 rounded-lg bg-primary/10 text-primary">
|
|
5966
|
+
{icons[icon]}
|
|
5967
|
+
</div>
|
|
5968
|
+
<div>
|
|
5969
|
+
<h3 className="text-h4 text-foreground group-hover:text-primary transition-colors">
|
|
5970
|
+
{title}
|
|
5971
|
+
</h3>
|
|
5972
|
+
<p className="text-body-sm text-muted-foreground mt-1">
|
|
5973
|
+
{description}
|
|
5974
|
+
</p>
|
|
5975
|
+
</div>
|
|
5976
|
+
</div>
|
|
5977
|
+
</a>
|
|
5978
|
+
)
|
|
5979
|
+
}
|
|
5980
|
+
|
|
5981
|
+
function Principle({
|
|
5982
|
+
title,
|
|
5983
|
+
description,
|
|
5984
|
+
}: {
|
|
5985
|
+
title: string
|
|
5986
|
+
description: string
|
|
5987
|
+
}) {
|
|
5988
|
+
return (
|
|
5989
|
+
<div className="p-4 rounded-lg border border-border">
|
|
5990
|
+
<h3 className="text-h4 text-foreground mb-1">{title}</h3>
|
|
5991
|
+
<p className="text-body-sm text-muted-foreground">{description}</p>
|
|
5992
|
+
</div>
|
|
5993
|
+
)
|
|
5994
|
+
}
|
|
5995
|
+
`;
|
|
5996
|
+
await writeFile(path18.join(appDir, "src/app/page.tsx"), content);
|
|
5997
|
+
}
|
|
5998
|
+
async function generateColorsPage(config, appDir) {
|
|
5999
|
+
const content = `export default function ColorsPage() {
|
|
6000
|
+
return (
|
|
6001
|
+
<div className="p-8 max-w-5xl">
|
|
6002
|
+
<header className="mb-12">
|
|
6003
|
+
<h1 className="text-display-01 text-foreground mb-4">Colors</h1>
|
|
6004
|
+
<p className="text-body-lg text-muted-foreground max-w-2xl">
|
|
6005
|
+
The color system is built on a foundation of brand colors
|
|
6006
|
+
with semantic tokens for consistent application across all interfaces.
|
|
6007
|
+
</p>
|
|
6008
|
+
</header>
|
|
6009
|
+
|
|
6010
|
+
<section className="mb-12">
|
|
6011
|
+
<h2 className="text-h2 text-foreground mb-6">Brand Colors</h2>
|
|
6012
|
+
<p className="text-body text-muted-foreground mb-6">
|
|
6013
|
+
Core brand colors used across all products.
|
|
6014
|
+
</p>
|
|
6015
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
6016
|
+
<ColorCard
|
|
6017
|
+
name="Primary"
|
|
6018
|
+
cssVar="--primary"
|
|
6019
|
+
usage="CTAs, buttons, links, interactive elements"
|
|
6020
|
+
/>
|
|
6021
|
+
<ColorCard
|
|
6022
|
+
name="Secondary"
|
|
6023
|
+
cssVar="--secondary"
|
|
6024
|
+
usage="Secondary buttons, subtle backgrounds"
|
|
6025
|
+
/>
|
|
6026
|
+
<ColorCard
|
|
6027
|
+
name="Accent"
|
|
6028
|
+
cssVar="--accent"
|
|
6029
|
+
usage="Highlights, badges, special elements"
|
|
6030
|
+
/>
|
|
6031
|
+
</div>
|
|
6032
|
+
</section>
|
|
6033
|
+
|
|
6034
|
+
<section className="mb-12">
|
|
6035
|
+
<h2 className="text-h2 text-foreground mb-6">Semantic Colors</h2>
|
|
6036
|
+
<p className="text-body text-muted-foreground mb-6">
|
|
6037
|
+
Colors with specific meaning used for feedback and status indication.
|
|
6038
|
+
</p>
|
|
6039
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
6040
|
+
<ColorCard
|
|
6041
|
+
name="Success"
|
|
6042
|
+
cssVar="--success"
|
|
6043
|
+
usage="Success states, confirmations"
|
|
6044
|
+
/>
|
|
6045
|
+
<ColorCard
|
|
6046
|
+
name="Warning"
|
|
6047
|
+
cssVar="--warning"
|
|
6048
|
+
usage="Warnings, caution states"
|
|
6049
|
+
/>
|
|
6050
|
+
<ColorCard
|
|
6051
|
+
name="Destructive"
|
|
6052
|
+
cssVar="--destructive"
|
|
6053
|
+
usage="Errors, destructive actions"
|
|
6054
|
+
/>
|
|
6055
|
+
</div>
|
|
6056
|
+
</section>
|
|
6057
|
+
|
|
6058
|
+
<section className="mb-12">
|
|
6059
|
+
<h2 className="text-h2 text-foreground mb-6">Background Colors</h2>
|
|
6060
|
+
<p className="text-body text-muted-foreground mb-6">
|
|
6061
|
+
Surface colors for different elevation levels and contexts.
|
|
6062
|
+
</p>
|
|
6063
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
6064
|
+
<ColorCard
|
|
6065
|
+
name="Background"
|
|
6066
|
+
cssVar="--background"
|
|
6067
|
+
usage="Page background"
|
|
6068
|
+
/>
|
|
6069
|
+
<ColorCard
|
|
6070
|
+
name="Card"
|
|
6071
|
+
cssVar="--card"
|
|
6072
|
+
usage="Card surfaces"
|
|
6073
|
+
/>
|
|
6074
|
+
<ColorCard
|
|
6075
|
+
name="Muted"
|
|
6076
|
+
cssVar="--muted"
|
|
6077
|
+
usage="Subtle backgrounds"
|
|
6078
|
+
/>
|
|
6079
|
+
</div>
|
|
6080
|
+
</section>
|
|
6081
|
+
|
|
6082
|
+
<section className="mb-12">
|
|
6083
|
+
<h2 className="text-h2 text-foreground mb-6">Text Colors</h2>
|
|
6084
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
6085
|
+
<ColorCard
|
|
6086
|
+
name="Foreground"
|
|
6087
|
+
cssVar="--foreground"
|
|
6088
|
+
usage="Primary text, headings"
|
|
6089
|
+
/>
|
|
6090
|
+
<ColorCard
|
|
6091
|
+
name="Muted Foreground"
|
|
6092
|
+
cssVar="--muted-foreground"
|
|
6093
|
+
usage="Secondary text, descriptions"
|
|
6094
|
+
/>
|
|
6095
|
+
<ColorCard
|
|
6096
|
+
name="Primary"
|
|
6097
|
+
cssVar="--primary"
|
|
6098
|
+
usage="Links, emphasis"
|
|
6099
|
+
/>
|
|
6100
|
+
</div>
|
|
6101
|
+
</section>
|
|
6102
|
+
|
|
6103
|
+
<section>
|
|
6104
|
+
<h2 className="text-h2 text-foreground mb-6">CSS Variables</h2>
|
|
6105
|
+
<p className="text-body text-muted-foreground mb-4">
|
|
6106
|
+
Use these CSS variables in your styles for consistent theming:
|
|
6107
|
+
</p>
|
|
6108
|
+
<div className="bg-muted rounded-lg p-4 overflow-x-auto">
|
|
6109
|
+
<pre className="text-sm text-foreground">
|
|
6110
|
+
{\`/* Primary colors */
|
|
6111
|
+
var(--primary)
|
|
6112
|
+
var(--secondary)
|
|
6113
|
+
var(--accent)
|
|
6114
|
+
|
|
6115
|
+
/* Backgrounds */
|
|
6116
|
+
var(--background)
|
|
6117
|
+
var(--card)
|
|
6118
|
+
var(--muted)
|
|
6119
|
+
|
|
6120
|
+
/* Text */
|
|
6121
|
+
var(--foreground)
|
|
6122
|
+
var(--muted-foreground)
|
|
6123
|
+
|
|
6124
|
+
/* Semantic */
|
|
6125
|
+
var(--success)
|
|
6126
|
+
var(--warning)
|
|
6127
|
+
var(--destructive)
|
|
6128
|
+
|
|
6129
|
+
/* Borders */
|
|
6130
|
+
var(--border)
|
|
6131
|
+
var(--ring)\`}
|
|
6132
|
+
</pre>
|
|
6133
|
+
</div>
|
|
6134
|
+
</section>
|
|
6135
|
+
</div>
|
|
6136
|
+
)
|
|
6137
|
+
}
|
|
6138
|
+
|
|
6139
|
+
function ColorCard({
|
|
6140
|
+
name,
|
|
6141
|
+
cssVar,
|
|
6142
|
+
usage,
|
|
6143
|
+
}: {
|
|
6144
|
+
name: string
|
|
6145
|
+
cssVar: string
|
|
6146
|
+
usage: string
|
|
6147
|
+
}) {
|
|
6148
|
+
return (
|
|
6149
|
+
<div className="rounded-xl border border-border overflow-hidden">
|
|
6150
|
+
<div
|
|
6151
|
+
className="h-24"
|
|
6152
|
+
style={{ backgroundColor: \`var(\${cssVar})\` }}
|
|
6153
|
+
/>
|
|
6154
|
+
<div className="p-4 bg-card">
|
|
6155
|
+
<h3 className="text-h4 text-foreground mb-1">{name}</h3>
|
|
6156
|
+
<p className="text-xs text-muted-foreground mb-2">
|
|
6157
|
+
<code className="bg-muted px-1 rounded">{cssVar}</code>
|
|
6158
|
+
</p>
|
|
6159
|
+
<p className="text-xs text-muted-foreground">{usage}</p>
|
|
6160
|
+
</div>
|
|
6161
|
+
</div>
|
|
6162
|
+
)
|
|
6163
|
+
}
|
|
6164
|
+
`;
|
|
6165
|
+
await writeFile(path18.join(appDir, "src/app/colors/page.tsx"), content);
|
|
6166
|
+
}
|
|
6167
|
+
async function generateTypographyPage(config, appDir) {
|
|
6168
|
+
const { designSystem } = config;
|
|
6169
|
+
const headingFontName = designSystem.headingFont.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
6170
|
+
const bodyFontName = designSystem.bodyFont.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
6171
|
+
const content = `export default function TypographyPage() {
|
|
6172
|
+
return (
|
|
6173
|
+
<div className="p-8 max-w-5xl">
|
|
6174
|
+
<header className="mb-12">
|
|
6175
|
+
<h1 className="text-display-01 text-foreground mb-4">Typography</h1>
|
|
6176
|
+
<p className="text-body-lg text-muted-foreground max-w-2xl">
|
|
6177
|
+
The typography system uses ${headingFontName} for headings and ${bodyFontName}
|
|
6178
|
+
for body text, creating a modern and professional aesthetic.
|
|
6179
|
+
</p>
|
|
6180
|
+
</header>
|
|
6181
|
+
|
|
6182
|
+
<section className="mb-12">
|
|
6183
|
+
<h2 className="text-h2 text-foreground mb-6">Font Families</h2>
|
|
6184
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
6185
|
+
<FontFamilyCard
|
|
6186
|
+
name="${headingFontName}"
|
|
6187
|
+
variable="--font-heading"
|
|
6188
|
+
usage="Headings, display text"
|
|
6189
|
+
sample="The quick brown fox jumps over the lazy dog"
|
|
6190
|
+
/>
|
|
6191
|
+
<FontFamilyCard
|
|
6192
|
+
name="${bodyFontName}"
|
|
6193
|
+
variable="--font-body"
|
|
6194
|
+
usage="Body text, UI elements"
|
|
6195
|
+
sample="The quick brown fox jumps over the lazy dog"
|
|
6196
|
+
/>
|
|
6197
|
+
</div>
|
|
6198
|
+
</section>
|
|
6199
|
+
|
|
6200
|
+
<section className="mb-12">
|
|
6201
|
+
<h2 className="text-h2 text-foreground mb-6">Type Scale</h2>
|
|
6202
|
+
<p className="text-body text-muted-foreground mb-6">
|
|
6203
|
+
A comprehensive scale from display headings down to small body text.
|
|
6204
|
+
</p>
|
|
6205
|
+
<div className="space-y-6">
|
|
6206
|
+
<TypeScaleItem name="Display Hero" size="56px (3.5rem)" className="text-display-hero" />
|
|
6207
|
+
<TypeScaleItem name="Display 02" size="48px (3rem)" className="text-display-02" />
|
|
6208
|
+
<TypeScaleItem name="Display 01" size="40px (2.5rem)" className="text-display-01" />
|
|
6209
|
+
<TypeScaleItem name="Heading 1" size="32px (2rem)" className="text-h1" />
|
|
6210
|
+
<TypeScaleItem name="Heading 2" size="24px (1.5rem)" className="text-h2" />
|
|
6211
|
+
<TypeScaleItem name="Heading 3" size="20px (1.25rem)" className="text-h3" />
|
|
6212
|
+
<TypeScaleItem name="Heading 4" size="18px (1.125rem)" className="text-h4" />
|
|
6213
|
+
<TypeScaleItem name="Body Large" size="18px (1.125rem)" className="text-body-lg" />
|
|
6214
|
+
<TypeScaleItem name="Body" size="16px (1rem)" className="text-body" />
|
|
6215
|
+
<TypeScaleItem name="Body Small" size="14px (0.875rem)" className="text-body-sm" />
|
|
6216
|
+
</div>
|
|
6217
|
+
</section>
|
|
6218
|
+
|
|
6219
|
+
<section className="mb-12">
|
|
6220
|
+
<h2 className="text-h2 text-foreground mb-6">Font Weights</h2>
|
|
6221
|
+
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
6222
|
+
<WeightCard weight="400" name="Regular" />
|
|
6223
|
+
<WeightCard weight="500" name="Medium" />
|
|
6224
|
+
<WeightCard weight="600" name="Semibold" />
|
|
6225
|
+
<WeightCard weight="700" name="Bold" />
|
|
6226
|
+
</div>
|
|
6227
|
+
</section>
|
|
6228
|
+
|
|
6229
|
+
<section>
|
|
6230
|
+
<h2 className="text-h2 text-foreground mb-6">CSS Classes</h2>
|
|
6231
|
+
<p className="text-body text-muted-foreground mb-4">
|
|
6232
|
+
Use these utility classes for consistent typography:
|
|
6233
|
+
</p>
|
|
6234
|
+
<div className="bg-muted rounded-lg p-4 overflow-x-auto">
|
|
6235
|
+
<pre className="text-sm text-foreground">
|
|
6236
|
+
{\`/* Display & Headings */
|
|
6237
|
+
.text-display-hero /* 56px, weight 600 */
|
|
6238
|
+
.text-display-02 /* 48px, weight 600 */
|
|
6239
|
+
.text-display-01 /* 40px, weight 600 */
|
|
6240
|
+
.text-h1 /* 32px, weight 600 */
|
|
6241
|
+
.text-h2 /* 24px, weight 600 */
|
|
6242
|
+
.text-h3 /* 20px, weight 500 */
|
|
6243
|
+
.text-h4 /* 18px, weight 500 */
|
|
6244
|
+
|
|
6245
|
+
/* Body Text */
|
|
6246
|
+
.text-body-lg /* 18px, line-height 175% */
|
|
6247
|
+
.text-body /* 16px, line-height 150% */
|
|
6248
|
+
.text-body-sm /* 14px, line-height 150% */
|
|
6249
|
+
|
|
6250
|
+
/* Colors */
|
|
6251
|
+
.text-foreground /* Primary text color */
|
|
6252
|
+
.text-muted-foreground /* Secondary text color */
|
|
6253
|
+
.text-primary /* Brand primary color */\`}
|
|
6254
|
+
</pre>
|
|
6255
|
+
</div>
|
|
6256
|
+
</section>
|
|
6257
|
+
</div>
|
|
6258
|
+
)
|
|
6259
|
+
}
|
|
6260
|
+
|
|
6261
|
+
function FontFamilyCard({
|
|
6262
|
+
name,
|
|
6263
|
+
variable,
|
|
6264
|
+
usage,
|
|
6265
|
+
sample,
|
|
6266
|
+
}: {
|
|
6267
|
+
name: string
|
|
6268
|
+
variable: string
|
|
6269
|
+
usage: string
|
|
6270
|
+
sample: string
|
|
6271
|
+
}) {
|
|
6272
|
+
return (
|
|
6273
|
+
<div className="rounded-xl border border-border p-6">
|
|
6274
|
+
<h3 className="text-h3 text-foreground mb-2">{name}</h3>
|
|
6275
|
+
<p className="text-body-sm text-muted-foreground mb-4">{usage}</p>
|
|
6276
|
+
<div className="p-4 bg-muted rounded-lg mb-4">
|
|
6277
|
+
<p className="text-xl text-foreground">{sample}</p>
|
|
6278
|
+
</div>
|
|
6279
|
+
<code className="text-xs text-muted-foreground bg-muted px-2 py-1 rounded">
|
|
6280
|
+
{variable}
|
|
6281
|
+
</code>
|
|
6282
|
+
</div>
|
|
6283
|
+
)
|
|
6284
|
+
}
|
|
6285
|
+
|
|
6286
|
+
function TypeScaleItem({
|
|
6287
|
+
name,
|
|
6288
|
+
size,
|
|
6289
|
+
className,
|
|
6290
|
+
}: {
|
|
6291
|
+
name: string
|
|
6292
|
+
size: string
|
|
6293
|
+
className: string
|
|
6294
|
+
}) {
|
|
6295
|
+
return (
|
|
6296
|
+
<div className="flex flex-col md:flex-row md:items-center gap-4 pb-6 border-b border-border">
|
|
6297
|
+
<div className="md:w-48 shrink-0">
|
|
6298
|
+
<p className="text-body-sm font-medium text-foreground">{name}</p>
|
|
6299
|
+
<p className="text-xs text-muted-foreground">{size}</p>
|
|
6300
|
+
</div>
|
|
6301
|
+
<div className="flex-1 overflow-hidden">
|
|
6302
|
+
<p className={\`text-foreground truncate \${className}\`}>
|
|
6303
|
+
The quick brown fox jumps
|
|
6304
|
+
</p>
|
|
6305
|
+
</div>
|
|
6306
|
+
</div>
|
|
6307
|
+
)
|
|
6308
|
+
}
|
|
6309
|
+
|
|
6310
|
+
function WeightCard({ weight, name }: { weight: string; name: string }) {
|
|
6311
|
+
return (
|
|
6312
|
+
<div className="rounded-lg border border-border p-4 text-center">
|
|
6313
|
+
<p className="text-2xl text-foreground mb-2" style={{ fontWeight: weight }}>
|
|
6314
|
+
Aa
|
|
6315
|
+
</p>
|
|
6316
|
+
<p className="text-body-sm font-medium text-foreground">{name}</p>
|
|
6317
|
+
<p className="text-xs text-muted-foreground">{weight}</p>
|
|
6318
|
+
</div>
|
|
6319
|
+
)
|
|
6320
|
+
}
|
|
6321
|
+
`;
|
|
6322
|
+
await writeFile(path18.join(appDir, "src/app/typography/page.tsx"), content);
|
|
6323
|
+
}
|
|
6324
|
+
async function generateSpacingPage(config, appDir) {
|
|
6325
|
+
const { designSystem } = config;
|
|
6326
|
+
const radiusValue = radiusValues[designSystem.borderRadius];
|
|
6327
|
+
const content = `export default function SpacingPage() {
|
|
6328
|
+
return (
|
|
6329
|
+
<div className="p-8 max-w-5xl">
|
|
6330
|
+
<header className="mb-12">
|
|
6331
|
+
<h1 className="text-display-01 text-foreground mb-4">Spacing</h1>
|
|
6332
|
+
<p className="text-body-lg text-muted-foreground max-w-2xl">
|
|
6333
|
+
Consistent spacing creates visual harmony and improves readability.
|
|
6334
|
+
Uses Tailwind's default spacing scale with specific guidelines for common patterns.
|
|
6335
|
+
</p>
|
|
6336
|
+
</header>
|
|
6337
|
+
|
|
6338
|
+
<section className="mb-12">
|
|
6339
|
+
<h2 className="text-h2 text-foreground mb-6">Spacing Scale</h2>
|
|
6340
|
+
<p className="text-body text-muted-foreground mb-6">
|
|
6341
|
+
Based on a 4px base unit. Use these values for margins, padding, and gaps.
|
|
6342
|
+
</p>
|
|
6343
|
+
<div className="space-y-3">
|
|
6344
|
+
<SpacingRow value="1" pixels="4px" />
|
|
6345
|
+
<SpacingRow value="2" pixels="8px" />
|
|
6346
|
+
<SpacingRow value="3" pixels="12px" />
|
|
6347
|
+
<SpacingRow value="4" pixels="16px" />
|
|
6348
|
+
<SpacingRow value="6" pixels="24px" />
|
|
6349
|
+
<SpacingRow value="8" pixels="32px" />
|
|
6350
|
+
<SpacingRow value="12" pixels="48px" />
|
|
6351
|
+
<SpacingRow value="16" pixels="64px" />
|
|
6352
|
+
<SpacingRow value="24" pixels="96px" />
|
|
6353
|
+
</div>
|
|
6354
|
+
</section>
|
|
6355
|
+
|
|
6356
|
+
<section className="mb-12">
|
|
6357
|
+
<h2 className="text-h2 text-foreground mb-6">Border Radius</h2>
|
|
6358
|
+
<p className="text-body text-muted-foreground mb-6">
|
|
6359
|
+
Default border radius: <code className="bg-muted px-1 rounded">${radiusValue}</code>
|
|
6360
|
+
</p>
|
|
6361
|
+
<div className="grid grid-cols-2 md:grid-cols-5 gap-4">
|
|
6362
|
+
<RadiusCard name="XS" value="calc(var(--radius) * 0.5)" />
|
|
6363
|
+
<RadiusCard name="SM" value="calc(var(--radius) * 0.75)" />
|
|
6364
|
+
<RadiusCard name="Default" value="var(--radius)" />
|
|
6365
|
+
<RadiusCard name="LG" value="calc(var(--radius) * 1.5)" />
|
|
6366
|
+
<RadiusCard name="Pill" value="9999px" />
|
|
6367
|
+
</div>
|
|
6368
|
+
</section>
|
|
6369
|
+
|
|
6370
|
+
<section className="mb-12">
|
|
6371
|
+
<h2 className="text-h2 text-foreground mb-6">Common Patterns</h2>
|
|
6372
|
+
<div className="space-y-6">
|
|
6373
|
+
<PatternCard
|
|
6374
|
+
title="Page Padding"
|
|
6375
|
+
description="Standard page content padding"
|
|
6376
|
+
code="p-8 or px-8 py-12"
|
|
6377
|
+
/>
|
|
6378
|
+
<PatternCard
|
|
6379
|
+
title="Section Spacing"
|
|
6380
|
+
description="Space between major sections"
|
|
6381
|
+
code="mb-12 or space-y-12"
|
|
6382
|
+
/>
|
|
6383
|
+
<PatternCard
|
|
6384
|
+
title="Card Padding"
|
|
6385
|
+
description="Internal card padding"
|
|
6386
|
+
code="p-4 or p-6"
|
|
6387
|
+
/>
|
|
6388
|
+
<PatternCard
|
|
6389
|
+
title="Grid Gap"
|
|
6390
|
+
description="Space between grid items"
|
|
6391
|
+
code="gap-4 or gap-6"
|
|
6392
|
+
/>
|
|
6393
|
+
</div>
|
|
6394
|
+
</section>
|
|
6395
|
+
|
|
6396
|
+
<section>
|
|
6397
|
+
<h2 className="text-h2 text-foreground mb-6">CSS Variables</h2>
|
|
6398
|
+
<div className="bg-muted rounded-lg p-4 overflow-x-auto">
|
|
6399
|
+
<pre className="text-sm text-foreground">
|
|
6400
|
+
{\`/* Border Radius */
|
|
6401
|
+
--radius: ${radiusValue};
|
|
6402
|
+
--radius-xs: calc(var(--radius) * 0.5);
|
|
6403
|
+
--radius-sm: calc(var(--radius) * 0.75);
|
|
6404
|
+
--radius-lg: calc(var(--radius) * 1.5);
|
|
6405
|
+
--radius-pill: 9999px;\`}
|
|
6406
|
+
</pre>
|
|
6407
|
+
</div>
|
|
6408
|
+
</section>
|
|
6409
|
+
</div>
|
|
6410
|
+
)
|
|
6411
|
+
}
|
|
6412
|
+
|
|
6413
|
+
function SpacingRow({ value, pixels }: { value: string; pixels: string }) {
|
|
6414
|
+
return (
|
|
6415
|
+
<div className="flex items-center gap-4">
|
|
6416
|
+
<div className="w-16 text-right">
|
|
6417
|
+
<code className="text-sm text-muted-foreground">{value}</code>
|
|
6418
|
+
</div>
|
|
6419
|
+
<div className="flex-1 flex items-center gap-2">
|
|
6420
|
+
<div className="h-6 bg-primary rounded" style={{ width: pixels }} />
|
|
6421
|
+
<span className="text-sm text-muted-foreground">{pixels}</span>
|
|
6422
|
+
</div>
|
|
6423
|
+
</div>
|
|
6424
|
+
)
|
|
6425
|
+
}
|
|
6426
|
+
|
|
6427
|
+
function RadiusCard({ name, value }: { name: string; value: string }) {
|
|
6428
|
+
return (
|
|
6429
|
+
<div className="text-center">
|
|
6430
|
+
<div className="h-20 w-full bg-primary mb-3" style={{ borderRadius: value }} />
|
|
6431
|
+
<p className="text-body-sm font-medium text-foreground">{name}</p>
|
|
6432
|
+
<code className="text-xs text-muted-foreground">{value}</code>
|
|
6433
|
+
</div>
|
|
6434
|
+
)
|
|
6435
|
+
}
|
|
6436
|
+
|
|
6437
|
+
function PatternCard({
|
|
6438
|
+
title,
|
|
6439
|
+
description,
|
|
6440
|
+
code,
|
|
6441
|
+
}: {
|
|
6442
|
+
title: string
|
|
6443
|
+
description: string
|
|
6444
|
+
code: string
|
|
6445
|
+
}) {
|
|
6446
|
+
return (
|
|
6447
|
+
<div className="rounded-lg border border-border p-4">
|
|
6448
|
+
<h3 className="text-h4 text-foreground mb-1">{title}</h3>
|
|
6449
|
+
<p className="text-body-sm text-muted-foreground mb-2">{description}</p>
|
|
6450
|
+
<code className="text-xs text-primary bg-primary/10 px-2 py-1 rounded">{code}</code>
|
|
6451
|
+
</div>
|
|
6452
|
+
)
|
|
6453
|
+
}
|
|
6454
|
+
`;
|
|
6455
|
+
await writeFile(path18.join(appDir, "src/app/spacing/page.tsx"), content);
|
|
6456
|
+
}
|
|
6457
|
+
async function generateBrandPage(config, appDir) {
|
|
6458
|
+
const content = `export default function BrandPage() {
|
|
6459
|
+
return (
|
|
6460
|
+
<div className="p-8 max-w-5xl">
|
|
6461
|
+
<header className="mb-12">
|
|
6462
|
+
<h1 className="text-display-01 text-foreground mb-4">Brand</h1>
|
|
6463
|
+
<p className="text-body-lg text-muted-foreground max-w-2xl">
|
|
6464
|
+
Brand assets and guidelines for ${config.name}. Download logos, icons,
|
|
6465
|
+
and learn about proper usage.
|
|
6466
|
+
</p>
|
|
6467
|
+
</header>
|
|
6468
|
+
|
|
6469
|
+
<section className="mb-12">
|
|
6470
|
+
<h2 className="text-h2 text-foreground mb-6">Logo</h2>
|
|
6471
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
6472
|
+
<div className="rounded-xl border border-border p-8 bg-white flex items-center justify-center">
|
|
6473
|
+
<div className="h-16 w-16 rounded-xl bg-primary flex items-center justify-center text-primary-foreground text-2xl font-bold">
|
|
6474
|
+
${config.name.charAt(0).toUpperCase()}
|
|
6475
|
+
</div>
|
|
6476
|
+
</div>
|
|
6477
|
+
<div className="rounded-xl border border-border p-8 bg-zinc-900 flex items-center justify-center">
|
|
6478
|
+
<div className="h-16 w-16 rounded-xl bg-primary flex items-center justify-center text-primary-foreground text-2xl font-bold">
|
|
6479
|
+
${config.name.charAt(0).toUpperCase()}
|
|
6480
|
+
</div>
|
|
6481
|
+
</div>
|
|
6482
|
+
</div>
|
|
6483
|
+
<p className="text-body-sm text-muted-foreground mt-4">
|
|
6484
|
+
The logo should maintain adequate spacing from other elements. Minimum clear space
|
|
6485
|
+
equals the height of the logo mark.
|
|
6486
|
+
</p>
|
|
6487
|
+
</section>
|
|
6488
|
+
|
|
6489
|
+
<section className="mb-12">
|
|
6490
|
+
<h2 className="text-h2 text-foreground mb-6">Logo Usage</h2>
|
|
6491
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
6492
|
+
<UsageCard
|
|
6493
|
+
title="Do"
|
|
6494
|
+
items={[
|
|
6495
|
+
'Use the logo on clean backgrounds',
|
|
6496
|
+
'Maintain the aspect ratio',
|
|
6497
|
+
'Use approved color variants',
|
|
6498
|
+
'Keep adequate clear space',
|
|
6499
|
+
]}
|
|
6500
|
+
type="do"
|
|
6501
|
+
/>
|
|
6502
|
+
<UsageCard
|
|
6503
|
+
title="Don\\'t"
|
|
6504
|
+
items={[
|
|
6505
|
+
'Stretch or distort the logo',
|
|
6506
|
+
'Add effects like shadows or gradients',
|
|
6507
|
+
'Place on busy backgrounds',
|
|
6508
|
+
'Change the logo colors',
|
|
6509
|
+
]}
|
|
6510
|
+
type="dont"
|
|
6511
|
+
/>
|
|
6512
|
+
</div>
|
|
6513
|
+
</section>
|
|
6514
|
+
|
|
6515
|
+
<section>
|
|
6516
|
+
<h2 className="text-h2 text-foreground mb-6">Downloads</h2>
|
|
6517
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
6518
|
+
<DownloadCard
|
|
6519
|
+
title="Logo Pack"
|
|
6520
|
+
description="SVG, PNG in various sizes"
|
|
6521
|
+
icon="image"
|
|
6522
|
+
/>
|
|
6523
|
+
<DownloadCard
|
|
6524
|
+
title="Icon Set"
|
|
6525
|
+
description="App icons for all platforms"
|
|
6526
|
+
icon="grid"
|
|
6527
|
+
/>
|
|
6528
|
+
<DownloadCard
|
|
6529
|
+
title="Brand Guidelines"
|
|
6530
|
+
description="Complete brand documentation"
|
|
6531
|
+
icon="file"
|
|
6532
|
+
/>
|
|
6533
|
+
</div>
|
|
6534
|
+
</section>
|
|
6535
|
+
</div>
|
|
6536
|
+
)
|
|
6537
|
+
}
|
|
6538
|
+
|
|
6539
|
+
function UsageCard({
|
|
6540
|
+
title,
|
|
6541
|
+
items,
|
|
6542
|
+
type,
|
|
6543
|
+
}: {
|
|
6544
|
+
title: string
|
|
6545
|
+
items: string[]
|
|
6546
|
+
type: 'do' | 'dont'
|
|
6547
|
+
}) {
|
|
6548
|
+
const borderColor = type === 'do' ? 'border-l-success' : 'border-l-destructive'
|
|
6549
|
+
|
|
6550
|
+
return (
|
|
6551
|
+
<div className={\`rounded-lg border border-border border-l-4 \${borderColor} p-4\`}>
|
|
6552
|
+
<h3 className="text-h4 text-foreground mb-3">{title}</h3>
|
|
6553
|
+
<ul className="space-y-2">
|
|
6554
|
+
{items.map((item, i) => (
|
|
6555
|
+
<li key={i} className="flex items-start gap-2 text-body-sm text-muted-foreground">
|
|
6556
|
+
<span className={type === 'do' ? 'text-success' : 'text-destructive'}>
|
|
6557
|
+
{type === 'do' ? '\u2713' : '\u2717'}
|
|
6558
|
+
</span>
|
|
6559
|
+
{item}
|
|
6560
|
+
</li>
|
|
6561
|
+
))}
|
|
6562
|
+
</ul>
|
|
6563
|
+
</div>
|
|
6564
|
+
)
|
|
6565
|
+
}
|
|
6566
|
+
|
|
6567
|
+
function DownloadCard({
|
|
6568
|
+
title,
|
|
6569
|
+
description,
|
|
6570
|
+
icon,
|
|
6571
|
+
}: {
|
|
6572
|
+
title: string
|
|
6573
|
+
description: string
|
|
6574
|
+
icon: string
|
|
6575
|
+
}) {
|
|
6576
|
+
return (
|
|
6577
|
+
<button className="rounded-lg border border-border p-4 text-left hover:bg-muted transition-colors w-full">
|
|
6578
|
+
<div className="flex items-center gap-3">
|
|
6579
|
+
<div className="p-2 rounded-lg bg-primary/10 text-primary">
|
|
6580
|
+
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
6581
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
|
6582
|
+
</svg>
|
|
6583
|
+
</div>
|
|
6584
|
+
<div>
|
|
6585
|
+
<h3 className="text-h4 text-foreground">{title}</h3>
|
|
6586
|
+
<p className="text-body-sm text-muted-foreground">{description}</p>
|
|
6587
|
+
</div>
|
|
6588
|
+
</div>
|
|
6589
|
+
</button>
|
|
6590
|
+
)
|
|
6591
|
+
}
|
|
6592
|
+
`;
|
|
6593
|
+
await writeFile(path18.join(appDir, "src/app/brand/page.tsx"), content);
|
|
6594
|
+
}
|
|
6595
|
+
async function generateBlocksPage(appDir) {
|
|
6596
|
+
const content = `export default function BlocksPage() {
|
|
6597
|
+
return (
|
|
6598
|
+
<div className="p-8 max-w-5xl">
|
|
6599
|
+
<header className="mb-12">
|
|
6600
|
+
<h1 className="text-display-01 text-foreground mb-4">Blocks</h1>
|
|
6601
|
+
<p className="text-body-lg text-muted-foreground max-w-2xl">
|
|
6602
|
+
Pre-built UI blocks for common patterns. Copy and paste into your projects.
|
|
6603
|
+
</p>
|
|
6604
|
+
</header>
|
|
6605
|
+
|
|
6606
|
+
<section className="mb-12">
|
|
6607
|
+
<h2 className="text-h2 text-foreground mb-6">Marketing</h2>
|
|
6608
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
6609
|
+
<BlockCard title="Hero" description="Hero sections with CTA" />
|
|
6610
|
+
<BlockCard title="Features" description="Feature grids and lists" />
|
|
6611
|
+
<BlockCard title="Pricing" description="Pricing tables and cards" />
|
|
6612
|
+
<BlockCard title="Testimonials" description="Customer testimonials" />
|
|
6613
|
+
<BlockCard title="CTA" description="Call-to-action sections" />
|
|
6614
|
+
<BlockCard title="FAQ" description="FAQ accordions" />
|
|
6615
|
+
</div>
|
|
6616
|
+
</section>
|
|
6617
|
+
|
|
6618
|
+
<section className="mb-12">
|
|
6619
|
+
<h2 className="text-h2 text-foreground mb-6">Dashboard</h2>
|
|
6620
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
6621
|
+
<BlockCard title="Stats" description="Metric and stat cards" />
|
|
6622
|
+
<BlockCard title="Tables" description="Data tables with actions" />
|
|
6623
|
+
<BlockCard title="Charts" description="Chart components" />
|
|
6624
|
+
<BlockCard title="Sidebar" description="Navigation sidebars" />
|
|
6625
|
+
<BlockCard title="Header" description="Dashboard headers" />
|
|
6626
|
+
</div>
|
|
6627
|
+
</section>
|
|
6628
|
+
|
|
6629
|
+
<section>
|
|
6630
|
+
<h2 className="text-h2 text-foreground mb-6">Widgets</h2>
|
|
6631
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
6632
|
+
<BlockCard title="Chat" description="Chat windows and bubbles" />
|
|
6633
|
+
<BlockCard title="Forms" description="Contact and input forms" />
|
|
6634
|
+
<BlockCard title="Cards" description="Content cards" />
|
|
6635
|
+
</div>
|
|
6636
|
+
</section>
|
|
6637
|
+
</div>
|
|
6638
|
+
)
|
|
6639
|
+
}
|
|
6640
|
+
|
|
6641
|
+
function BlockCard({ title, description }: { title: string; description: string }) {
|
|
6642
|
+
return (
|
|
6643
|
+
<a
|
|
6644
|
+
href="#"
|
|
6645
|
+
className="group block rounded-lg border border-border p-4 hover:border-primary/30 hover:bg-muted/50 transition-all"
|
|
6646
|
+
>
|
|
6647
|
+
<h3 className="text-h4 text-foreground group-hover:text-primary transition-colors">
|
|
6648
|
+
{title}
|
|
6649
|
+
</h3>
|
|
6650
|
+
<p className="text-body-sm text-muted-foreground mt-1">{description}</p>
|
|
6651
|
+
</a>
|
|
6652
|
+
)
|
|
6653
|
+
}
|
|
6654
|
+
`;
|
|
6655
|
+
await writeFile(path18.join(appDir, "src/app/blocks/page.tsx"), content);
|
|
6656
|
+
}
|
|
6657
|
+
async function generateComponentsPage(appDir) {
|
|
6658
|
+
const content = `export default function ComponentsPage() {
|
|
6659
|
+
return (
|
|
6660
|
+
<div className="p-8 max-w-5xl">
|
|
6661
|
+
<header className="mb-12">
|
|
6662
|
+
<h1 className="text-display-01 text-foreground mb-4">Components</h1>
|
|
6663
|
+
<p className="text-body-lg text-muted-foreground max-w-2xl">
|
|
6664
|
+
A collection of reusable UI components built with shadcn/ui.
|
|
6665
|
+
Browse the sidebar to explore individual components.
|
|
6666
|
+
</p>
|
|
6667
|
+
</header>
|
|
6668
|
+
|
|
6669
|
+
<section className="mb-12">
|
|
6670
|
+
<h2 className="text-h2 text-foreground mb-6">Getting Started</h2>
|
|
6671
|
+
<div className="bg-muted rounded-lg p-6">
|
|
6672
|
+
<p className="text-body text-muted-foreground mb-4">
|
|
6673
|
+
Components are available from the @repo/ui package. Import them directly:
|
|
6674
|
+
</p>
|
|
6675
|
+
<pre className="text-sm text-foreground bg-background rounded p-4 overflow-x-auto">
|
|
6676
|
+
{\`import { Button } from '@repo/ui/button'
|
|
6677
|
+
import { Card, CardHeader, CardContent } from '@repo/ui/card'\`}
|
|
6678
|
+
</pre>
|
|
6679
|
+
</div>
|
|
6680
|
+
</section>
|
|
6681
|
+
|
|
6682
|
+
<section>
|
|
6683
|
+
<h2 className="text-h2 text-foreground mb-6">Categories</h2>
|
|
6684
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
6685
|
+
<CategoryCard
|
|
6686
|
+
title="Inputs"
|
|
6687
|
+
components={['Button', 'Input', 'Textarea', 'Select', 'Checkbox', 'Radio Group', 'Switch', 'Slider']}
|
|
6688
|
+
/>
|
|
6689
|
+
<CategoryCard
|
|
6690
|
+
title="Display"
|
|
6691
|
+
components={['Card', 'Badge', 'Avatar', 'Alert', 'Progress', 'Skeleton', 'Table']}
|
|
6692
|
+
/>
|
|
6693
|
+
<CategoryCard
|
|
6694
|
+
title="Navigation"
|
|
6695
|
+
components={['Tabs', 'Breadcrumb', 'Pagination', 'Navigation Menu', 'Sidebar']}
|
|
6696
|
+
/>
|
|
6697
|
+
<CategoryCard
|
|
6698
|
+
title="Overlays"
|
|
6699
|
+
components={['Dialog', 'Drawer', 'Sheet', 'Popover', 'Tooltip', 'Dropdown Menu']}
|
|
6700
|
+
/>
|
|
6701
|
+
</div>
|
|
6702
|
+
</section>
|
|
6703
|
+
</div>
|
|
6704
|
+
)
|
|
6705
|
+
}
|
|
6706
|
+
|
|
6707
|
+
function CategoryCard({
|
|
6708
|
+
title,
|
|
6709
|
+
components,
|
|
6710
|
+
}: {
|
|
6711
|
+
title: string
|
|
6712
|
+
components: string[]
|
|
6713
|
+
}) {
|
|
6714
|
+
return (
|
|
6715
|
+
<div className="rounded-xl border border-border p-6">
|
|
6716
|
+
<h3 className="text-h3 text-foreground mb-4">{title}</h3>
|
|
6717
|
+
<div className="flex flex-wrap gap-2">
|
|
6718
|
+
{components.map((component) => {
|
|
6719
|
+
const slug = component.toLowerCase().replace(/\\s+/g, '-')
|
|
6720
|
+
return (
|
|
6721
|
+
<a
|
|
6722
|
+
key={slug}
|
|
6723
|
+
href={\`/components/\${slug}\`}
|
|
6724
|
+
className="px-3 py-1 rounded-full bg-muted text-sm text-muted-foreground hover:text-foreground hover:bg-muted/80 transition-colors"
|
|
6725
|
+
>
|
|
6726
|
+
{component}
|
|
6727
|
+
</a>
|
|
6728
|
+
)
|
|
6729
|
+
})}
|
|
6730
|
+
</div>
|
|
6731
|
+
</div>
|
|
6732
|
+
)
|
|
6733
|
+
}
|
|
6734
|
+
`;
|
|
6735
|
+
await writeFile(path18.join(appDir, "src/app/components/page.tsx"), content);
|
|
6736
|
+
}
|
|
6737
|
+
async function generateUtils(appDir) {
|
|
6738
|
+
const content = `import { clsx, type ClassValue } from 'clsx'
|
|
6739
|
+
import { twMerge } from 'tailwind-merge'
|
|
6740
|
+
|
|
6741
|
+
export function cn(...inputs: ClassValue[]) {
|
|
6742
|
+
return twMerge(clsx(inputs))
|
|
6743
|
+
}
|
|
6744
|
+
`;
|
|
6745
|
+
await writeFile(path18.join(appDir, "src/lib/utils.ts"), content);
|
|
6746
|
+
}
|
|
6747
|
+
|
|
4857
6748
|
// src/generators/index.ts
|
|
4858
6749
|
async function generateProject(config) {
|
|
4859
6750
|
const spinner = ora();
|
|
@@ -4894,7 +6785,7 @@ async function generateSingleProject(config, spinner) {
|
|
|
4894
6785
|
await generateShadcn(config, targetDir);
|
|
4895
6786
|
spinner.succeed("shadcn/ui configured");
|
|
4896
6787
|
spinner.start("Setting up Convex...");
|
|
4897
|
-
await generateConvex(config,
|
|
6788
|
+
await generateConvex(config, path19.join(targetDir, "convex"));
|
|
4898
6789
|
spinner.succeed("Convex configured");
|
|
4899
6790
|
spinner.start("Configuring Better-Auth...");
|
|
4900
6791
|
await generateBetterAuth(config, targetDir);
|
|
@@ -4937,7 +6828,7 @@ async function generateMonorepoProject(config, spinner) {
|
|
|
4937
6828
|
spinner.start("Creating shared UI package...");
|
|
4938
6829
|
await generateUIPackage(config, targetDir);
|
|
4939
6830
|
spinner.succeed("Shared UI package created");
|
|
4940
|
-
const webDir =
|
|
6831
|
+
const webDir = path19.join(targetDir, "apps/web");
|
|
4941
6832
|
spinner.start("Generating web application...");
|
|
4942
6833
|
await generateBaseNextjs(config, webDir);
|
|
4943
6834
|
await generateTailwind(config, webDir);
|
|
@@ -4947,10 +6838,10 @@ async function generateMonorepoProject(config, spinner) {
|
|
|
4947
6838
|
await generateEmail(config, webDir);
|
|
4948
6839
|
await updateWebTsConfig(webDir);
|
|
4949
6840
|
spinner.succeed("Web application generated");
|
|
4950
|
-
const backendDir =
|
|
6841
|
+
const backendDir = path19.join(targetDir, "packages/backend");
|
|
4951
6842
|
spinner.start("Setting up Convex backend...");
|
|
4952
6843
|
await ensureDir(backendDir);
|
|
4953
|
-
await generateConvex(config,
|
|
6844
|
+
await generateConvex(config, path19.join(backendDir, "convex"));
|
|
4954
6845
|
spinner.succeed("Convex backend configured");
|
|
4955
6846
|
if (config.integrations.analytics !== "none") {
|
|
4956
6847
|
spinner.start(`Setting up ${config.integrations.analytics} analytics...`);
|
|
@@ -4973,7 +6864,7 @@ async function generateMonorepoProject(config, spinner) {
|
|
|
4973
6864
|
spinner.succeed(`${config.integrations.monitoring} monitoring configured`);
|
|
4974
6865
|
}
|
|
4975
6866
|
if (config.marketingSite !== "none") {
|
|
4976
|
-
const marketingDir =
|
|
6867
|
+
const marketingDir = path19.join(targetDir, "apps/marketing");
|
|
4977
6868
|
spinner.start(`Generating ${config.marketingSite} marketing site...`);
|
|
4978
6869
|
if (config.marketingSite === "payload") {
|
|
4979
6870
|
await generatePayload(config, marketingDir);
|
|
@@ -4985,11 +6876,14 @@ async function generateMonorepoProject(config, spinner) {
|
|
|
4985
6876
|
spinner.succeed(`${config.marketingSite} marketing site generated`);
|
|
4986
6877
|
}
|
|
4987
6878
|
if (config.includeDocs) {
|
|
4988
|
-
const docsDir =
|
|
6879
|
+
const docsDir = path19.join(targetDir, "apps/docs");
|
|
4989
6880
|
spinner.start("Generating Fumadocs documentation site...");
|
|
4990
6881
|
await generateFumadocs(config, docsDir);
|
|
4991
6882
|
spinner.succeed("Fumadocs documentation site generated");
|
|
4992
6883
|
}
|
|
6884
|
+
spinner.start("Generating design system app...");
|
|
6885
|
+
await generateDesignSystemApp(config, targetDir);
|
|
6886
|
+
spinner.succeed("Design system app generated");
|
|
4993
6887
|
await generateGitignore(targetDir);
|
|
4994
6888
|
}
|
|
4995
6889
|
async function runPostGenerationSteps(config, spinner) {
|
|
@@ -5003,7 +6897,7 @@ async function runPostGenerationSteps(config, spinner) {
|
|
|
5003
6897
|
}
|
|
5004
6898
|
spinner.start("Installing shadcn/ui components...");
|
|
5005
6899
|
try {
|
|
5006
|
-
const shadcnDir = config.structure === "monorepo" ?
|
|
6900
|
+
const shadcnDir = config.structure === "monorepo" ? path19.join(targetDir, "packages/ui") : targetDir;
|
|
5007
6901
|
await runShadcnAdd(shadcnDir);
|
|
5008
6902
|
spinner.succeed("shadcn/ui components installed");
|
|
5009
6903
|
} catch (error) {
|
|
@@ -5063,7 +6957,7 @@ async function generateBiomeConfig(targetDir) {
|
|
|
5063
6957
|
}
|
|
5064
6958
|
};
|
|
5065
6959
|
await writeFile(
|
|
5066
|
-
|
|
6960
|
+
path19.join(targetDir, "biome.json"),
|
|
5067
6961
|
JSON.stringify(biomeJson, null, 2)
|
|
5068
6962
|
);
|
|
5069
6963
|
}
|
|
@@ -5114,13 +7008,13 @@ yarn-error.log*
|
|
|
5114
7008
|
*.pem
|
|
5115
7009
|
.cache
|
|
5116
7010
|
`;
|
|
5117
|
-
await writeFile(
|
|
7011
|
+
await writeFile(path19.join(targetDir, ".gitignore"), gitignoreContent);
|
|
5118
7012
|
}
|
|
5119
7013
|
async function generateHuskyHooks(targetDir) {
|
|
5120
7014
|
const preCommitContent = `pnpm lint-staged
|
|
5121
7015
|
`;
|
|
5122
|
-
await ensureDir(
|
|
5123
|
-
await writeFile(
|
|
7016
|
+
await ensureDir(path19.join(targetDir, ".husky"));
|
|
7017
|
+
await writeFile(path19.join(targetDir, ".husky/pre-commit"), preCommitContent);
|
|
5124
7018
|
}
|
|
5125
7019
|
async function updateWebTsConfig(webDir) {
|
|
5126
7020
|
const tsConfig = {
|
|
@@ -5134,14 +7028,14 @@ async function updateWebTsConfig(webDir) {
|
|
|
5134
7028
|
exclude: ["node_modules"]
|
|
5135
7029
|
};
|
|
5136
7030
|
await writeFile(
|
|
5137
|
-
|
|
7031
|
+
path19.join(webDir, "tsconfig.json"),
|
|
5138
7032
|
JSON.stringify(tsConfig, null, 2)
|
|
5139
7033
|
);
|
|
5140
7034
|
}
|
|
5141
7035
|
async function generateNextjsMarketing(config, marketingDir) {
|
|
5142
|
-
await ensureDir(
|
|
5143
|
-
await ensureDir(
|
|
5144
|
-
await ensureDir(
|
|
7036
|
+
await ensureDir(path19.join(marketingDir, "src/app"));
|
|
7037
|
+
await ensureDir(path19.join(marketingDir, "src/components"));
|
|
7038
|
+
await ensureDir(path19.join(marketingDir, "public"));
|
|
5145
7039
|
const packageJson = {
|
|
5146
7040
|
name: "@repo/marketing",
|
|
5147
7041
|
version: "0.1.0",
|
|
@@ -5172,7 +7066,7 @@ async function generateNextjsMarketing(config, marketingDir) {
|
|
|
5172
7066
|
}
|
|
5173
7067
|
};
|
|
5174
7068
|
await writeFile(
|
|
5175
|
-
|
|
7069
|
+
path19.join(marketingDir, "package.json"),
|
|
5176
7070
|
JSON.stringify(packageJson, null, 2)
|
|
5177
7071
|
);
|
|
5178
7072
|
const homePageContent = `export default function HomePage() {
|
|
@@ -5195,7 +7089,7 @@ async function generateNextjsMarketing(config, marketingDir) {
|
|
|
5195
7089
|
}
|
|
5196
7090
|
`;
|
|
5197
7091
|
await writeFile(
|
|
5198
|
-
|
|
7092
|
+
path19.join(marketingDir, "src/app/page.tsx"),
|
|
5199
7093
|
homePageContent
|
|
5200
7094
|
);
|
|
5201
7095
|
const layoutContent = `import type { Metadata } from 'next'
|
|
@@ -5219,7 +7113,7 @@ export default function RootLayout({
|
|
|
5219
7113
|
}
|
|
5220
7114
|
`;
|
|
5221
7115
|
await writeFile(
|
|
5222
|
-
|
|
7116
|
+
path19.join(marketingDir, "src/app/layout.tsx"),
|
|
5223
7117
|
layoutContent
|
|
5224
7118
|
);
|
|
5225
7119
|
const globalsCss = `@import "tailwindcss";
|
|
@@ -5231,7 +7125,7 @@ export default function RootLayout({
|
|
|
5231
7125
|
}
|
|
5232
7126
|
`;
|
|
5233
7127
|
await writeFile(
|
|
5234
|
-
|
|
7128
|
+
path19.join(marketingDir, "src/app/globals.css"),
|
|
5235
7129
|
globalsCss
|
|
5236
7130
|
);
|
|
5237
7131
|
const tsConfig = {
|
|
@@ -5245,7 +7139,7 @@ export default function RootLayout({
|
|
|
5245
7139
|
exclude: ["node_modules"]
|
|
5246
7140
|
};
|
|
5247
7141
|
await writeFile(
|
|
5248
|
-
|
|
7142
|
+
path19.join(marketingDir, "tsconfig.json"),
|
|
5249
7143
|
JSON.stringify(tsConfig, null, 2)
|
|
5250
7144
|
);
|
|
5251
7145
|
const nextConfig = `import type { NextConfig } from 'next'
|
|
@@ -5256,14 +7150,14 @@ const nextConfig: NextConfig = {
|
|
|
5256
7150
|
|
|
5257
7151
|
export default nextConfig
|
|
5258
7152
|
`;
|
|
5259
|
-
await writeFile(
|
|
7153
|
+
await writeFile(path19.join(marketingDir, "next.config.ts"), nextConfig);
|
|
5260
7154
|
const postcssConfig = `export default {
|
|
5261
7155
|
plugins: {
|
|
5262
7156
|
'@tailwindcss/postcss': {},
|
|
5263
7157
|
},
|
|
5264
7158
|
}
|
|
5265
7159
|
`;
|
|
5266
|
-
await writeFile(
|
|
7160
|
+
await writeFile(path19.join(marketingDir, "postcss.config.mjs"), postcssConfig);
|
|
5267
7161
|
}
|
|
5268
7162
|
function displaySuccessMessage(config) {
|
|
5269
7163
|
const apps = ["web"];
|
|
@@ -5274,6 +7168,7 @@ function displaySuccessMessage(config) {
|
|
|
5274
7168
|
if (config.includeDocs) {
|
|
5275
7169
|
apps.push("docs");
|
|
5276
7170
|
}
|
|
7171
|
+
apps.push("design-system");
|
|
5277
7172
|
}
|
|
5278
7173
|
console.log();
|
|
5279
7174
|
p6.outro(pc2.green("Project created successfully!"));
|