create-bearnie 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +13 -2
  2. package/dist/index.js +216 -5
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- ****# create-bearnie
1
+ # create-bearnie
2
2
 
3
3
  Create a new Astro project with Bearnie UI components pre-configured.
4
4
 
@@ -12,13 +12,23 @@ npx create-bearnie
12
12
  npm create bearnie my-app
13
13
  ```
14
14
 
15
+ ## Custom Theme
16
+
17
+ Create your own theme at [bearnie.dev/create](https://bearnie.dev/create), then use the generated command:
18
+
19
+ ```bash
20
+ npm create bearnie my-app --theme=https://bearnie.dev/create#YOUR_THEME_HASH
21
+ ```
22
+
23
+ This applies your custom colors, radius, and font to the project's CSS.
24
+
15
25
  ## What's included
16
26
 
17
27
  - Astro 5 with TypeScript
18
28
  - Tailwind CSS v4 (via `@tailwindcss/vite`)
19
29
  - Bearnie CSS variables and theme
20
30
  - Simple landing page to get started
21
- - Ready for components via `npx @bearnie/cli add`
31
+ - Ready for components via `npx bearnie add`
22
32
 
23
33
  ## After creating your project
24
34
 
@@ -32,5 +42,6 @@ npm run dev
32
42
  ## Learn more
33
43
 
34
44
  - [Bearnie Documentation](https://bearnie.dev/docs)
45
+ - [Theme Builder](https://bearnie.dev/create)
35
46
  - [Browse Components](https://bearnie.dev/docs/components)
36
47
  - [GitHub](https://github.com/michael-andreuzza/bearnie)
package/dist/index.js CHANGED
@@ -10,13 +10,217 @@ var __dirname = path.dirname(fileURLToPath(import.meta.url));
10
10
  var amber = (text) => pc.yellow(text);
11
11
  var logo = `${amber("\u{1F43B}")} ${pc.bold("bearnie")}`;
12
12
  var link = (text, url) => `\x1B]8;;${url}\x07${pc.cyan(text)}\x1B]8;;\x07`;
13
+ function parseThemeArg() {
14
+ const themeArg = process.argv.find((arg) => arg.startsWith("--theme="));
15
+ if (!themeArg) return null;
16
+ return themeArg.split("=")[1];
17
+ }
18
+ function extractThemeHash(themeUrl) {
19
+ try {
20
+ const hashIndex = themeUrl.indexOf("#");
21
+ if (hashIndex === -1) return null;
22
+ return themeUrl.slice(hashIndex + 1);
23
+ } catch {
24
+ return null;
25
+ }
26
+ }
27
+ function decodeThemeConfig(hash) {
28
+ try {
29
+ const decoded = Buffer.from(hash, "base64").toString("utf-8");
30
+ return JSON.parse(decoded);
31
+ } catch {
32
+ return null;
33
+ }
34
+ }
35
+ function oklch(l, c, h) {
36
+ return `oklch(${l} ${c} ${h})`;
37
+ }
38
+ function generateThemeCSS(config) {
39
+ const radiusRem = config.radius / 16;
40
+ const fontFamily = config.font === "geist" ? "'Geist', sans-serif" : "Inter, sans-serif";
41
+ const neutralChroma = config.neutralHue > 0 ? 0.01 : 0;
42
+ const lightPrimary = config.primaryHue === 0 ? "oklch(0.205 0 0)" : oklch(0.55, 0.2, config.primaryHue);
43
+ const lightPrimaryFg = "oklch(0.985 0 0)";
44
+ const darkPrimary = config.primaryHue === 0 ? "oklch(0.985 0 0)" : oklch(0.7, 0.2, config.primaryHue);
45
+ const darkPrimaryFg = "oklch(0.205 0 0)";
46
+ return `@import "tailwindcss";
47
+ @plugin "@tailwindcss/forms";
48
+
49
+ @theme {
50
+ /* Typography */
51
+ --font-sans: ${fontFamily};
52
+ --font-mono: Geist Mono, monospace;
53
+
54
+ /* Border Radius - Base: ${radiusRem}rem */
55
+ --radius-sm: calc(var(--radius) - 4px);
56
+ --radius-md: calc(var(--radius) - 2px);
57
+ --radius-lg: var(--radius);
58
+ --radius-xl: calc(var(--radius) + 4px);
59
+ --radius-2xl: calc(var(--radius) + 8px);
60
+ --radius-full: 9999px;
61
+
62
+ /* Shadows */
63
+ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
64
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
65
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
66
+ }
67
+
68
+ @theme inline {
69
+ /* Semantic Colors */
70
+ --color-background: var(--background);
71
+ --color-foreground: var(--foreground);
72
+ --color-card: var(--card);
73
+ --color-card-foreground: var(--card-foreground);
74
+ --color-popover: var(--popover);
75
+ --color-popover-foreground: var(--popover-foreground);
76
+ --color-primary: var(--primary);
77
+ --color-primary-foreground: var(--primary-foreground);
78
+ --color-secondary: var(--secondary);
79
+ --color-secondary-foreground: var(--secondary-foreground);
80
+ --color-muted: var(--muted);
81
+ --color-muted-foreground: var(--muted-foreground);
82
+ --color-accent: var(--accent);
83
+ --color-accent-foreground: var(--accent-foreground);
84
+ --color-destructive: var(--destructive);
85
+ --color-destructive-foreground: var(--destructive-foreground);
86
+ --color-success: var(--success);
87
+ --color-success-foreground: var(--success-foreground);
88
+ --color-warning: var(--warning);
89
+ --color-warning-foreground: var(--warning-foreground);
90
+ --color-info: var(--info);
91
+ --color-info-foreground: var(--info-foreground);
92
+ --color-border: var(--border);
93
+ --color-input: var(--input);
94
+ --color-ring: var(--ring);
95
+ --color-chart-1: var(--chart-1);
96
+ --color-chart-2: var(--chart-2);
97
+ --color-chart-3: var(--chart-3);
98
+ --color-chart-4: var(--chart-4);
99
+ --color-chart-5: var(--chart-5);
100
+
101
+ /* Sidebar Colors */
102
+ --color-sidebar: var(--sidebar);
103
+ --color-sidebar-foreground: var(--sidebar-foreground);
104
+ --color-sidebar-primary: var(--sidebar-primary);
105
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
106
+ --color-sidebar-accent: var(--sidebar-accent);
107
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
108
+ --color-sidebar-border: var(--sidebar-border);
109
+ --color-sidebar-ring: var(--sidebar-ring);
110
+ }
111
+
112
+ :root {
113
+ --radius: ${radiusRem}rem;
114
+ --background: ${oklch(1, neutralChroma, config.neutralHue)};
115
+ --foreground: ${oklch(0.145, neutralChroma, config.neutralHue)};
116
+ --card: ${oklch(1, neutralChroma, config.neutralHue)};
117
+ --card-foreground: ${oklch(0.145, neutralChroma, config.neutralHue)};
118
+ --popover: ${oklch(1, neutralChroma, config.neutralHue)};
119
+ --popover-foreground: ${oklch(0.145, neutralChroma, config.neutralHue)};
120
+ --primary: ${lightPrimary};
121
+ --primary-foreground: ${lightPrimaryFg};
122
+ --secondary: ${oklch(0.97, neutralChroma, config.neutralHue)};
123
+ --secondary-foreground: ${oklch(0.205, neutralChroma, config.neutralHue)};
124
+ --muted: ${oklch(0.97, neutralChroma, config.neutralHue)};
125
+ --muted-foreground: ${oklch(0.556, neutralChroma, config.neutralHue)};
126
+ --accent: ${oklch(0.97, neutralChroma, config.neutralHue)};
127
+ --accent-foreground: ${oklch(0.205, neutralChroma, config.neutralHue)};
128
+ --destructive: ${oklch(0.577, 0.245, config.destructiveHue)};
129
+ --destructive-foreground: ${oklch(0.577, 0.245, config.destructiveHue)};
130
+ --success: ${oklch(0.564, 0.168, config.successHue)};
131
+ --success-foreground: ${oklch(0.564, 0.168, config.successHue)};
132
+ --warning: ${oklch(0.681, 0.15, config.warningHue)};
133
+ --warning-foreground: ${oklch(0.681, 0.15, config.warningHue)};
134
+ --info: ${oklch(0.609, 0.202, config.infoHue)};
135
+ --info-foreground: ${oklch(0.609, 0.202, config.infoHue)};
136
+ --border: ${oklch(0.922, neutralChroma, config.neutralHue)};
137
+ --input: ${oklch(0.922, neutralChroma, config.neutralHue)};
138
+ --ring: ${oklch(0.708, neutralChroma, config.neutralHue)};
139
+ --chart-1: oklch(0.646 0.222 41.116);
140
+ --chart-2: oklch(0.6 0.118 184.704);
141
+ --chart-3: oklch(0.398 0.07 227.392);
142
+ --chart-4: oklch(0.828 0.189 84.429);
143
+ --chart-5: oklch(0.769 0.188 70.08);
144
+
145
+ /* Sidebar */
146
+ --sidebar: ${oklch(0.985, neutralChroma, config.neutralHue)};
147
+ --sidebar-foreground: ${oklch(0.145, neutralChroma, config.neutralHue)};
148
+ --sidebar-primary: ${lightPrimary};
149
+ --sidebar-primary-foreground: ${lightPrimaryFg};
150
+ --sidebar-accent: ${oklch(0.97, neutralChroma, config.neutralHue)};
151
+ --sidebar-accent-foreground: ${oklch(0.205, neutralChroma, config.neutralHue)};
152
+ --sidebar-border: ${oklch(0.922, neutralChroma, config.neutralHue)};
153
+ --sidebar-ring: ${oklch(0.708, neutralChroma, config.neutralHue)};
154
+ }
155
+
156
+ .dark {
157
+ --background: ${oklch(0.145, neutralChroma, config.neutralHue)};
158
+ --foreground: ${oklch(0.985, neutralChroma, config.neutralHue)};
159
+ --card: ${oklch(0.145, neutralChroma, config.neutralHue)};
160
+ --card-foreground: ${oklch(0.985, neutralChroma, config.neutralHue)};
161
+ --popover: ${oklch(0.145, neutralChroma, config.neutralHue)};
162
+ --popover-foreground: ${oklch(0.985, neutralChroma, config.neutralHue)};
163
+ --primary: ${darkPrimary};
164
+ --primary-foreground: ${darkPrimaryFg};
165
+ --secondary: ${oklch(0.269, neutralChroma, config.neutralHue)};
166
+ --secondary-foreground: ${oklch(0.985, neutralChroma, config.neutralHue)};
167
+ --muted: ${oklch(0.269, neutralChroma, config.neutralHue)};
168
+ --muted-foreground: ${oklch(0.708, neutralChroma, config.neutralHue)};
169
+ --accent: ${oklch(0.269, neutralChroma, config.neutralHue)};
170
+ --accent-foreground: ${oklch(0.985, neutralChroma, config.neutralHue)};
171
+ --destructive: ${oklch(0.65, 0.245, config.destructiveHue)};
172
+ --destructive-foreground: oklch(0.985 0 0);
173
+ --success: ${oklch(0.634, 0.168, config.successHue)};
174
+ --success-foreground: oklch(0.985 0 0);
175
+ --warning: ${oklch(0.785, 0.15, config.warningHue)};
176
+ --warning-foreground: oklch(0.985 0 0);
177
+ --info: ${oklch(0.701, 0.202, config.infoHue)};
178
+ --info-foreground: oklch(0.985 0 0);
179
+ --border: ${oklch(0.269, neutralChroma, config.neutralHue)};
180
+ --input: ${oklch(0.269, neutralChroma, config.neutralHue)};
181
+ --ring: ${oklch(0.439, neutralChroma, config.neutralHue)};
182
+ --chart-1: oklch(0.488 0.243 264.376);
183
+ --chart-2: oklch(0.696 0.17 162.48);
184
+ --chart-3: oklch(0.769 0.188 70.08);
185
+ --chart-4: oklch(0.627 0.265 303.9);
186
+ --chart-5: oklch(0.645 0.246 16.439);
187
+
188
+ /* Sidebar */
189
+ --sidebar: ${oklch(0.205, neutralChroma, config.neutralHue)};
190
+ --sidebar-foreground: ${oklch(0.985, neutralChroma, config.neutralHue)};
191
+ --sidebar-primary: ${config.primaryHue === 0 ? "oklch(0.488 0.243 264.376)" : oklch(0.7, 0.2, config.primaryHue)};
192
+ --sidebar-primary-foreground: oklch(0.985 0 0);
193
+ --sidebar-accent: ${oklch(0.269, neutralChroma, config.neutralHue)};
194
+ --sidebar-accent-foreground: ${oklch(0.985, neutralChroma, config.neutralHue)};
195
+ --sidebar-border: ${oklch(0.269, neutralChroma, config.neutralHue)};
196
+ --sidebar-ring: ${oklch(0.439, neutralChroma, config.neutralHue)};
197
+ }
198
+
199
+ /* Base styles */
200
+ * {
201
+ border-color: var(--border);
202
+ }
203
+ `;
204
+ }
13
205
  async function main() {
206
+ const themeUrl = parseThemeArg();
207
+ let themeConfig = null;
208
+ if (themeUrl) {
209
+ const hash = extractThemeHash(themeUrl);
210
+ if (hash) {
211
+ themeConfig = decodeThemeConfig(hash);
212
+ }
213
+ }
14
214
  console.log(`
15
215
  ${logo}
16
216
 
17
217
  ${amber("Hey!")} Let's create your Bearnie project.
218
+ ${themeConfig ? ` ${pc.dim("Using custom theme from URL")}
219
+ ` : ""}
18
220
  `);
19
- let projectName = process.argv[2];
221
+ let projectName = process.argv.find(
222
+ (arg, index) => index >= 2 && !arg.startsWith("--")
223
+ );
20
224
  if (!projectName) {
21
225
  const response = await prompts({
22
226
  type: "text",
@@ -38,12 +242,13 @@ async function main() {
38
242
  }
39
243
  projectName = response.projectName;
40
244
  }
41
- const targetDir = path.resolve(process.cwd(), projectName);
245
+ const finalName = projectName;
246
+ const targetDir = path.resolve(process.cwd(), finalName);
42
247
  if (fs.existsSync(targetDir)) {
43
248
  const { overwrite } = await prompts({
44
249
  type: "confirm",
45
250
  name: "overwrite",
46
- message: `Directory ${pc.cyan(projectName)} already exists. Overwrite?`,
251
+ message: `Directory ${pc.cyan(finalName)} already exists. Overwrite?`,
47
252
  initial: false
48
253
  });
49
254
  if (!overwrite) {
@@ -61,8 +266,14 @@ async function main() {
61
266
  await fs.copy(templateDir, targetDir);
62
267
  const pkgPath = path.join(targetDir, "package.json");
63
268
  const pkg = await fs.readJson(pkgPath);
64
- pkg.name = projectName;
269
+ pkg.name = finalName;
65
270
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
271
+ if (themeConfig) {
272
+ const globalCssPath = path.join(targetDir, "src", "styles", "global.css");
273
+ const customCSS = generateThemeCSS(themeConfig);
274
+ await fs.writeFile(globalCssPath, customCSS);
275
+ console.log(` ${pc.green("\u2713")} Applied custom theme`);
276
+ }
66
277
  await fs.writeFile(
67
278
  path.join(targetDir, ".gitignore"),
68
279
  `# Dependencies
@@ -94,7 +305,7 @@ Thumbs.db
94
305
 
95
306
  ${pc.bold("Next steps:")}
96
307
 
97
- ${pc.dim("1.")} cd ${pc.cyan(projectName)}
308
+ ${pc.dim("1.")} cd ${pc.cyan(finalName)}
98
309
  ${pc.dim("2.")} npm install
99
310
  ${pc.dim("3.")} npx bearnie add button card
100
311
  ${pc.dim("4.")} npm run dev
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-bearnie",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Create a new Astro project with Bearnie UI components",
5
5
  "type": "module",
6
6
  "license": "MIT",