create-kofi-stack 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/LICENSE +1 -1
  2. package/dist/index.js +187 -32
  3. package/package.json +1 -1
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 theodenanyoh
3
+ Copyright (c) 2026 Theo Denanyoh
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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
  );
@@ -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.1.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: config.shadcn.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: "hugeicons",
3196
+ iconLibrary: config.shadcn.iconLibrary,
3042
3197
  aliases: {
3043
3198
  components: "@/components",
3044
3199
  utils: "@/lib/utils",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-kofi-stack",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Scaffold opinionated full-stack projects with Next.js, Convex, Better-Auth, and more",
5
5
  "type": "module",
6
6
  "bin": {