create-aziz-app 2.0.3 → 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -62,10 +62,22 @@ async function main() {
62
62
  const includeApiExamples = await question(' Include example API routes? (Y/n): ');
63
63
  const includeDocs = await question(' Include documentation? (Y/n): ');
64
64
 
65
+ // Ask for CSS framework
66
+ console.log('\nšŸŽØ CSS Framework:');
67
+ console.log(' 1. None (custom CSS)');
68
+ console.log(' 2. Bootstrap 5');
69
+ console.log(' 3. Tailwind CSS');
70
+ const cssChoice = await question(' Choose (1/2/3): ');
71
+
72
+ let cssFramework = 'none';
73
+ if (cssChoice === '2') cssFramework = 'bootstrap';
74
+ else if (cssChoice === '3') cssFramework = 'tailwind';
75
+
65
76
  const features = {
66
77
  examples: includeExamples.toLowerCase() !== 'n' && includeExamples.toLowerCase() !== 'no',
67
78
  apiExamples: includeApiExamples.toLowerCase() !== 'n' && includeApiExamples.toLowerCase() !== 'no',
68
- docs: includeDocs.toLowerCase() !== 'n' && includeDocs.toLowerCase() !== 'no'
79
+ docs: includeDocs.toLowerCase() !== 'n' && includeDocs.toLowerCase() !== 'no',
80
+ cssFramework: cssFramework
69
81
  };
70
82
 
71
83
  console.log(`\n⚔ Creating "${projectName}"...\n`);
@@ -81,6 +93,9 @@ async function main() {
81
93
  // Update dev server port
82
94
  updateDevServerPort(targetDir, port);
83
95
 
96
+ // Setup CSS framework
97
+ setupCSSFramework(targetDir, features.cssFramework);
98
+
84
99
  console.log(`āœ… Done! Your project is ready.\n`);
85
100
  console.log(`šŸŽ‰ Welcome to Aziz App - A React-like Framework\n`);
86
101
  console.log(`šŸ“ Created project: ${projectName}\n`);
@@ -104,8 +119,13 @@ async function main() {
104
119
  console.log(` • File-based Routing - pages/About.jsx → #/about`);
105
120
  console.log(` • API Routes - Next.js-like API system`);
106
121
  console.log(` • Auto-Import - No manual script tags!`);
107
- console.log(` • Live Reload - Instant browser refresh\n`);
108
- console.log(`šŸ’” Quick tips:\n`);
122
+ console.log(` • Live Reload - Instant browser refresh`);
123
+ if (features.cssFramework === 'bootstrap') {
124
+ console.log(` • Bootstrap 5 - Responsive CSS framework`);
125
+ } else if (features.cssFramework === 'tailwind') {
126
+ console.log(` • Tailwind CSS - Utility-first CSS framework`);
127
+ }
128
+ console.log(`\nšŸ’” Quick tips:\n`);
109
129
  console.log(` • Create pages/MyPage.jsx → Auto-imported!`);
110
130
  console.log(` • Create api/todos.js → Auto-imported!`);
111
131
  console.log(` • Just save and browser refreshes automatically`);
@@ -170,6 +190,504 @@ function updateDevServerPort(targetDir, port) {
170
190
  }
171
191
  }
172
192
 
193
+ // Setup CSS framework
194
+ function setupCSSFramework(targetDir, framework) {
195
+ const htmlPath = path.join(targetDir, 'app.html');
196
+ if (!fs.existsSync(htmlPath)) return;
197
+
198
+ let html = fs.readFileSync(htmlPath, 'utf8');
199
+
200
+ if (framework === 'bootstrap') {
201
+ // Replace globals.css with Bootstrap CDN
202
+ html = html.replace(
203
+ '<link rel="stylesheet" href="styles/globals.css">',
204
+ `<!-- Bootstrap 5 CSS -->
205
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
206
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">`
207
+ );
208
+
209
+ // Add Bootstrap JS before closing body tag
210
+ const bootstrapJS = `
211
+ <!-- Bootstrap 5 JS -->
212
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
213
+
214
+ `;
215
+ html = html.replace('</body>', bootstrapJS + '</body>');
216
+
217
+ // Update components
218
+ updateComponentsForBootstrap(targetDir);
219
+
220
+ } else if (framework === 'tailwind') {
221
+ // Replace globals.css with Tailwind CSS
222
+ html = html.replace(
223
+ '<link rel="stylesheet" href="styles/globals.css">',
224
+ `<!-- Tailwind CSS -->
225
+ <link rel="stylesheet" href="styles/tailwind.css">
226
+ <script src="https://cdn.tailwindcss.com"></script>
227
+ <script>
228
+ tailwind.config = {
229
+ theme: {
230
+ extend: {
231
+ colors: {
232
+ primary: '#61dafb',
233
+ dark: '#282c34'
234
+ }
235
+ }
236
+ }
237
+ }
238
+ </script>`
239
+ );
240
+
241
+ // Update components
242
+ updateComponentsForTailwind(targetDir);
243
+ }
244
+ // If 'none', keep globals.css as is
245
+
246
+ fs.writeFileSync(htmlPath, html);
247
+ }
248
+
249
+ // Update components for Bootstrap
250
+ function updateComponentsForBootstrap(targetDir) {
251
+ // Update Navbar
252
+ const navbarPath = path.join(targetDir, 'components', 'Navbar.jsx');
253
+ if (fs.existsSync(navbarPath)) {
254
+ const navbarContent = `/** @jsx h */
255
+
256
+ function Navbar() {
257
+ const path = getCurrentPath();
258
+
259
+ return (
260
+ <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
261
+ <div class="container-fluid">
262
+ <span class="navbar-brand" onClick={() => navigate("home")} style={{ cursor: 'pointer' }}>
263
+ ⚔ AzizApp
264
+ </span>
265
+ <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
266
+ <span class="navbar-toggler-icon"></span>
267
+ </button>
268
+ <div class="collapse navbar-collapse" id="navbarNav">
269
+ <ul class="navbar-nav">
270
+ <li class="nav-item">
271
+ <a href="#/home" class={\`nav-link \${path === "home" ? "active" : ""}\`}>Home</a>
272
+ </li>
273
+ <li class="nav-item">
274
+ <a href="#/about" class={\`nav-link \${path === "about" ? "active" : ""}\`}>About</a>
275
+ </li>
276
+ <li class="nav-item">
277
+ <a href="#/contact" class={\`nav-link \${path === "contact" ? "active" : ""}\`}>Contact</a>
278
+ </li>
279
+ <li class="nav-item">
280
+ <a href="#/test" class={\`nav-link \${path === "test" ? "active" : ""}\`}>Test</a>
281
+ </li>
282
+ <li class="nav-item">
283
+ <a href="#/api-demo" class={\`nav-link \${path === "api-demo" ? "active" : ""}\`}>API Demo</a>
284
+ </li>
285
+ </ul>
286
+ </div>
287
+ </div>
288
+ </nav>
289
+ );
290
+ }
291
+ `;
292
+ fs.writeFileSync(navbarPath, navbarContent);
293
+ }
294
+
295
+ // Update Footer
296
+ const footerPath = path.join(targetDir, 'components', 'Footer.jsx');
297
+ if (fs.existsSync(footerPath)) {
298
+ const footerContent = `/** @jsx h */
299
+
300
+ function Footer() {
301
+ return (
302
+ <footer class="bg-dark text-light text-center py-3 mt-auto">
303
+ <div class="container">
304
+ <p class="mb-0">Made with ⚔ AzizApp</p>
305
+ </div>
306
+ </footer>
307
+ );
308
+ }
309
+ `;
310
+ fs.writeFileSync(footerPath, footerContent);
311
+ }
312
+
313
+ // Update all pages
314
+ updatePagesForBootstrap(targetDir);
315
+ }
316
+
317
+ // Update components for Tailwind
318
+ function updateComponentsForTailwind(targetDir) {
319
+ // Update Navbar
320
+ const navbarPath = path.join(targetDir, 'components', 'Navbar.jsx');
321
+ if (fs.existsSync(navbarPath)) {
322
+ const navbarContent = `/** @jsx h */
323
+
324
+ function Navbar() {
325
+ const path = getCurrentPath();
326
+
327
+ return (
328
+ <nav class="bg-dark shadow-lg">
329
+ <div class="container mx-auto px-4">
330
+ <div class="flex items-center justify-between h-16">
331
+ <span
332
+ class="text-primary text-xl font-bold cursor-pointer hover:text-blue-300 transition"
333
+ onClick={() => navigate("home")}
334
+ >
335
+ ⚔ AzizApp
336
+ </span>
337
+ <div class="flex space-x-4">
338
+ <a href="#/home" class={\`px-3 py-2 rounded-md text-sm font-medium transition \${path === "home" ? "bg-primary text-dark" : "text-gray-300 hover:bg-gray-700 hover:text-white"}\`}>
339
+ Home
340
+ </a>
341
+ <a href="#/about" class={\`px-3 py-2 rounded-md text-sm font-medium transition \${path === "about" ? "bg-primary text-dark" : "text-gray-300 hover:bg-gray-700 hover:text-white"}\`}>
342
+ About
343
+ </a>
344
+ <a href="#/contact" class={\`px-3 py-2 rounded-md text-sm font-medium transition \${path === "contact" ? "bg-primary text-dark" : "text-gray-300 hover:bg-gray-700 hover:text-white"}\`}>
345
+ Contact
346
+ </a>
347
+ <a href="#/test" class={\`px-3 py-2 rounded-md text-sm font-medium transition \${path === "test" ? "bg-primary text-dark" : "text-gray-300 hover:bg-gray-700 hover:text-white"}\`}>
348
+ Test
349
+ </a>
350
+ <a href="#/api-demo" class={\`px-3 py-2 rounded-md text-sm font-medium transition \${path === "api-demo" ? "bg-primary text-dark" : "text-gray-300 hover:bg-gray-700 hover:text-white"}\`}>
351
+ API Demo
352
+ </a>
353
+ </div>
354
+ </div>
355
+ </div>
356
+ </nav>
357
+ );
358
+ }
359
+ `;
360
+ fs.writeFileSync(navbarPath, navbarContent);
361
+ }
362
+
363
+ // Update Footer
364
+ const footerPath = path.join(targetDir, 'components', 'Footer.jsx');
365
+ if (fs.existsSync(footerPath)) {
366
+ const footerContent = `/** @jsx h */
367
+
368
+ function Footer() {
369
+ return (
370
+ <footer class="bg-dark text-gray-400 text-center py-4 mt-auto">
371
+ <div class="container mx-auto">
372
+ <p>Made with ⚔ AzizApp</p>
373
+ </div>
374
+ </footer>
375
+ );
376
+ }
377
+ `;
378
+ fs.writeFileSync(footerPath, footerContent);
379
+ }
380
+
381
+ // Update all pages
382
+ updatePagesForTailwind(targetDir);
383
+ }
384
+
385
+ // Update all pages for Bootstrap
386
+ function updatePagesForBootstrap(targetDir) {
387
+ const pagesDir = path.join(targetDir, 'pages');
388
+ if (!fs.existsSync(pagesDir)) return;
389
+
390
+ // Update Home.jsx
391
+ const homePath = path.join(pagesDir, 'Home.jsx');
392
+ if (fs.existsSync(homePath)) {
393
+ fs.writeFileSync(homePath, getBootstrapHomePage());
394
+ }
395
+
396
+ // Update test.jsx
397
+ const testPath = path.join(pagesDir, 'test.jsx');
398
+ if (fs.existsSync(testPath)) {
399
+ fs.writeFileSync(testPath, getBootstrapTestPage());
400
+ }
401
+
402
+ // About and Contact pages are simple, they work with any framework
403
+ }
404
+
405
+ // Update all pages for Tailwind
406
+ function updatePagesForTailwind(targetDir) {
407
+ const pagesDir = path.join(targetDir, 'pages');
408
+ if (!fs.existsSync(pagesDir)) return;
409
+
410
+ // Update Home.jsx
411
+ const homePath = path.join(pagesDir, 'Home.jsx');
412
+ if (fs.existsSync(homePath)) {
413
+ fs.writeFileSync(homePath, getTailwindHomePage());
414
+ }
415
+
416
+ // Update test.jsx
417
+ const testPath = path.join(pagesDir, 'test.jsx');
418
+ if (fs.existsSync(testPath)) {
419
+ fs.writeFileSync(testPath, getTailwindTestPage());
420
+ }
421
+ }
422
+
423
+ // Bootstrap Home Page
424
+ function getBootstrapHomePage() {
425
+ return `/** @jsx h */
426
+
427
+ registerPage("home", HomePage);
428
+
429
+ function HomePage() {
430
+ return (
431
+ <div class="container py-5">
432
+ <h1 class="display-4 mb-4">Welcome Home šŸ‘‹</h1>
433
+
434
+ <div class="card mb-4">
435
+ <div class="card-body">
436
+ <h2 class="card-title">Global State Counter</h2>
437
+ <p class="card-text">This uses the global state. Click the buttons to update it.</p>
438
+
439
+ <div class="text-center my-4">
440
+ <h1 class="display-1 fw-bold text-primary">{state.count}</h1>
441
+ </div>
442
+
443
+ <div class="d-flex gap-2 justify-content-center">
444
+ <button class="btn btn-danger" onClick={() => setState({ count: state.count - 1 })}>āˆ’</button>
445
+ <button class="btn btn-primary" onClick={() => setState({ count: state.count + 1 })}>+</button>
446
+ <button class="btn btn-secondary" onClick={() => setState({ count: 0 })}>Reset</button>
447
+ </div>
448
+ </div>
449
+ </div>
450
+
451
+ <div class="card">
452
+ <div class="card-body">
453
+ <h2 class="card-title">File-based Routing</h2>
454
+ <p class="card-text">
455
+ Each file in <strong>pages/</strong> is a route — just like Next.js.
456
+ Add a new file, run the build script, and it's a new page.
457
+ </p>
458
+ <div class="d-flex gap-2">
459
+ <button class="btn btn-primary" onClick={() => navigate("test")}>
460
+ Go to Test Page →
461
+ </button>
462
+ <button class="btn btn-outline-primary" onClick={() => navigate("about")}>
463
+ Go to About →
464
+ </button>
465
+ </div>
466
+ </div>
467
+ </div>
468
+ </div>
469
+ );
470
+ }
471
+ `;
472
+ }
473
+
474
+ // Tailwind Home Page
475
+ function getTailwindHomePage() {
476
+ return `/** @jsx h */
477
+
478
+ registerPage("home", HomePage);
479
+
480
+ function HomePage() {
481
+ return (
482
+ <div class="container mx-auto py-10 px-4">
483
+ <h1 class="text-4xl font-bold mb-6 text-gray-800">Welcome Home šŸ‘‹</h1>
484
+
485
+ <div class="bg-white rounded-lg shadow-md p-6 mb-6">
486
+ <h2 class="text-2xl font-bold mb-3 text-gray-800">Global State Counter</h2>
487
+ <p class="text-gray-600 mb-4">This uses the global state. Click the buttons to update it.</p>
488
+
489
+ <div class="text-center my-6">
490
+ <h1 class="text-7xl font-bold text-primary">{state.count}</h1>
491
+ </div>
492
+
493
+ <div class="flex gap-3 justify-center">
494
+ <button class="px-6 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition" onClick={() => setState({ count: state.count - 1 })}>āˆ’</button>
495
+ <button class="px-6 py-2 bg-primary text-dark font-bold rounded-lg hover:opacity-80 transition" onClick={() => setState({ count: state.count + 1 })}>+</button>
496
+ <button class="px-6 py-2 bg-gray-400 text-white rounded-lg hover:bg-gray-500 transition" onClick={() => setState({ count: 0 })}>Reset</button>
497
+ </div>
498
+ </div>
499
+
500
+ <div class="bg-white rounded-lg shadow-md p-6">
501
+ <h2 class="text-2xl font-bold mb-3 text-gray-800">File-based Routing</h2>
502
+ <p class="text-gray-600 mb-4">
503
+ Each file in <strong>pages/</strong> is a route — just like Next.js.
504
+ Add a new file, run the build script, and it's a new page.
505
+ </p>
506
+ <div class="flex gap-3">
507
+ <button class="px-6 py-2 bg-primary text-dark font-bold rounded-lg hover:opacity-80 transition" onClick={() => navigate("test")}>
508
+ Go to Test Page →
509
+ </button>
510
+ <button class="px-6 py-2 border-2 border-primary text-primary rounded-lg hover:bg-primary hover:text-dark transition" onClick={() => navigate("about")}>
511
+ Go to About →
512
+ </button>
513
+ </div>
514
+ </div>
515
+ </div>
516
+ );
517
+ }
518
+ `;
519
+ }
520
+
521
+ // Bootstrap Test Page
522
+ function getBootstrapTestPage() {
523
+ return `/** @jsx h */
524
+
525
+ registerPage("test", TestPage);
526
+
527
+ function TestPage() {
528
+ const [count, setCount] = useState(0);
529
+ const [name, setName] = useState('');
530
+ const [email, setEmail] = useState('');
531
+
532
+ const handleSubmit = (e) => {
533
+ e.preventDefault();
534
+ alert(\`Form submitted!\\nName: \${name}\\nEmail: \${email}\\nCount: \${count}\`);
535
+ };
536
+
537
+ return (
538
+ <div class="container py-5">
539
+ <h1 class="display-4 mb-4">Test Page 🧪</h1>
540
+
541
+ <div class="card mb-4">
542
+ <div class="card-body">
543
+ <h2 class="card-title">Counter Example</h2>
544
+ <p class="card-text">This demonstrates the state management system.</p>
545
+
546
+ <div class="text-center my-4">
547
+ <h1 class="display-1 fw-bold text-primary">{count}</h1>
548
+ </div>
549
+ <div class="d-flex gap-2 justify-content-center">
550
+ <button class="btn btn-primary" onClick={() => setCount(count + 1)}>
551
+ Increment
552
+ </button>
553
+ <button class="btn btn-danger" onClick={() => setCount(0)}>
554
+ Reset
555
+ </button>
556
+ </div>
557
+ </div>
558
+ </div>
559
+
560
+ <div class="card mb-4">
561
+ <div class="card-body">
562
+ <h2 class="card-title">Form Example</h2>
563
+ <p class="card-text">Try the form with state management.</p>
564
+
565
+ <form onSubmit={handleSubmit}>
566
+ <div class="mb-3">
567
+ <label class="form-label">Name:</label>
568
+ <input
569
+ type="text"
570
+ class="form-control"
571
+ value={name}
572
+ onInput={(e) => setName(e.target.value)}
573
+ placeholder="Enter your name"
574
+ />
575
+ </div>
576
+
577
+ <div class="mb-3">
578
+ <label class="form-label">Email:</label>
579
+ <input
580
+ type="email"
581
+ class="form-control"
582
+ value={email}
583
+ onInput={(e) => setEmail(e.target.value)}
584
+ placeholder="Enter your email"
585
+ />
586
+ </div>
587
+
588
+ <button type="submit" class="btn btn-primary">
589
+ Submit Form
590
+ </button>
591
+ </form>
592
+ </div>
593
+ </div>
594
+
595
+ <div class="card">
596
+ <div class="card-body">
597
+ <h2 class="card-title">State Information</h2>
598
+ <p class="mb-2">Current count: <strong class="text-primary">{count}</strong></p>
599
+ <p class="mb-2">Current name: <strong class="text-primary">{name || 'Not set'}</strong></p>
600
+ <p class="mb-0">Current email: <strong class="text-primary">{email || 'Not set'}</strong></p>
601
+ </div>
602
+ </div>
603
+ </div>
604
+ );
605
+ }
606
+ `;
607
+ }
608
+
609
+ // Tailwind Test Page
610
+ function getTailwindTestPage() {
611
+ return `/** @jsx h */
612
+
613
+ registerPage("test", TestPage);
614
+
615
+ function TestPage() {
616
+ const [count, setCount] = useState(0);
617
+ const [name, setName] = useState('');
618
+ const [email, setEmail] = useState('');
619
+
620
+ const handleSubmit = (e) => {
621
+ e.preventDefault();
622
+ alert(\`Form submitted!\\nName: \${name}\\nEmail: \${email}\\nCount: \${count}\`);
623
+ };
624
+
625
+ return (
626
+ <div class="container mx-auto py-10 px-4">
627
+ <h1 class="text-4xl font-bold mb-6 text-gray-800">Test Page 🧪</h1>
628
+
629
+ <div class="bg-white rounded-lg shadow-md p-6 mb-6">
630
+ <h2 class="text-2xl font-bold mb-3 text-gray-800">Counter Example</h2>
631
+ <p class="text-gray-600 mb-4">This demonstrates the state management system.</p>
632
+
633
+ <div class="text-center my-6">
634
+ <h1 class="text-7xl font-bold text-primary">{count}</h1>
635
+ </div>
636
+ <div class="flex gap-3 justify-center">
637
+ <button class="px-6 py-2 bg-primary text-dark font-bold rounded-lg hover:opacity-80 transition" onClick={() => setCount(count + 1)}>
638
+ Increment
639
+ </button>
640
+ <button class="px-6 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition" onClick={() => setCount(0)}>
641
+ Reset
642
+ </button>
643
+ </div>
644
+ </div>
645
+
646
+ <div class="bg-white rounded-lg shadow-md p-6 mb-6">
647
+ <h2 class="text-2xl font-bold mb-3 text-gray-800">Form Example</h2>
648
+ <p class="text-gray-600 mb-4">Try the form with state management.</p>
649
+
650
+ <form onSubmit={handleSubmit} class="space-y-4">
651
+ <div>
652
+ <label class="block text-sm font-medium text-gray-700 mb-1">Name:</label>
653
+ <input
654
+ type="text"
655
+ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary outline-none transition"
656
+ value={name}
657
+ onInput={(e) => setName(e.target.value)}
658
+ placeholder="Enter your name"
659
+ />
660
+ </div>
661
+
662
+ <div>
663
+ <label class="block text-sm font-medium text-gray-700 mb-1">Email:</label>
664
+ <input
665
+ type="email"
666
+ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary outline-none transition"
667
+ value={email}
668
+ onInput={(e) => setEmail(e.target.value)}
669
+ placeholder="Enter your email"
670
+ />
671
+ </div>
672
+
673
+ <button type="submit" class="px-6 py-2 bg-primary text-dark font-bold rounded-lg hover:opacity-80 transition">
674
+ Submit Form
675
+ </button>
676
+ </form>
677
+ </div>
678
+
679
+ <div class="bg-white rounded-lg shadow-md p-6">
680
+ <h2 class="text-2xl font-bold mb-3 text-gray-800">State Information</h2>
681
+ <p class="mb-2 text-gray-700">Current count: <strong class="text-primary">{count}</strong></p>
682
+ <p class="mb-2 text-gray-700">Current name: <strong class="text-primary">{name || 'Not set'}</strong></p>
683
+ <p class="mb-0 text-gray-700">Current email: <strong class="text-primary">{email || 'Not set'}</strong></p>
684
+ </div>
685
+ </div>
686
+ );
687
+ }
688
+ `;
689
+ }
690
+
173
691
  // Run main function
174
692
  main().catch(err => {
175
693
  console.error('āŒ Error:', err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-aziz-app",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "description": "A React-like framework with state management, routing, and API routes - Create apps with auto-import, live reload, and Next.js-like features",
5
5
  "keywords": [
6
6
  "react",
@@ -122,6 +122,14 @@ const response = await fetchApi('/api/todos');
122
122
  const data = await response.json();
123
123
  ```
124
124
 
125
+ ### CSS Frameworks
126
+ Choose your preferred styling:
127
+ - **Custom CSS** - Built-in responsive styles
128
+ - **Bootstrap 5** - Popular component framework
129
+ - **Tailwind CSS** - Utility-first framework
130
+
131
+ The CLI will automatically set up your chosen framework!
132
+
125
133
  ### Auto-Import
126
134
  - Create files in `pages/`, `components/`, or `api/`
127
135
  - Dev server automatically detects them