diff --git a/bin/cli.js b/bin/cli.js
index c3785e8..da3ec26 100644
--- a/bin/cli.js
+++ b/bin/cli.js
@@ -22,6 +22,10 @@ program
"--folders",
"Generate recommended folder structure"
)
+ .option(
+ "--tailwind",
+ "Set up Tailwind CSS"
+ )
.parse();
const projectName = program.args[0];
diff --git a/src/cleaners/cra.js b/src/cleaners/cra.js
index eb2a1c5..86d5c92 100644
--- a/src/cleaners/cra.js
+++ b/src/cleaners/cra.js
@@ -3,13 +3,10 @@ const {
writeFile,
} = require("../utils/files");
-function cleanCRA(projectPath, typescript) {
+function cleanCRA(projectPath, typescript, tailwind) {
const ext = typescript ? "tsx" : "js";
- writeFile(
- projectPath,
- `src/App.${ext}`,
-`function App() {
+ const appContent = `function App() {
return (
App
@@ -18,16 +15,19 @@ function cleanCRA(projectPath, typescript) {
}
export default App;
-`
- );
+`;
+
+ writeFile(projectPath, `src/App.${ext}`, appContent);
+
+ const indexCSSImport = tailwind ? `import "./index.css";\n` : "";
writeFile(
projectPath,
`src/index.${ext}`,
-typescript
-? `import React from "react";
+ typescript
+ ? `import React from "react";
import ReactDOM from "react-dom/client";
-import App from "./App";
+${indexCSSImport}import App from "./App";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
@@ -39,9 +39,9 @@ root.render(
);
`
-: `import React from "react";
+ : `import React from "react";
import ReactDOM from "react-dom/client";
-import App from "./App";
+${indexCSSImport}import App from "./App";
const root = ReactDOM.createRoot(
document.getElementById("root")
@@ -69,4 +69,4 @@ root.render(
});
}
-module.exports = cleanCRA;
\ No newline at end of file
+module.exports = cleanCRA;
diff --git a/src/creators/vite.js b/src/creators/vite.js
index 6b3c6d1..a98ad37 100644
--- a/src/creators/vite.js
+++ b/src/creators/vite.js
@@ -36,6 +36,15 @@ export default defineConfig({
})
`;
+const VITE_CONFIG_JS_TAILWIND = `import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+import tailwindcss from '@tailwindcss/vite'
+
+export default defineConfig({
+ plugins: [react(), tailwindcss()],
+})
+`;
+
const INDEX_HTML = (projectName) => `
@@ -86,6 +95,30 @@ ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
)
`;
+const MAIN_JSX_TAILWIND = `import React from 'react'
+import ReactDOM from 'react-dom/client'
+import './index.css'
+import App from './App'
+
+ReactDOM.createRoot(document.getElementById('root')).render(
+
+
+
+)
+`;
+
+const MAIN_TSX_TAILWIND = `import React from 'react'
+import ReactDOM from 'react-dom/client'
+import './index.css'
+import App from './App'
+
+ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
+
+
+
+)
+`;
+
const APP_JSX = `function App() {
return (
@@ -97,6 +130,7 @@ const APP_JSX = `function App() {
export default App
`;
+
const TSCONFIG = JSON.stringify({
compilerOptions: {
target: "ES2020",
@@ -118,7 +152,7 @@ const TSCONFIG = JSON.stringify({
include: ["src"],
}, null, 2);
-function createVite(projectName, typescript) {
+function createVite(projectName, typescript, tailwind) {
const projectPath = path.join(process.cwd(), projectName);
const ext = typescript ? "tsx" : "jsx";
@@ -135,7 +169,7 @@ function createVite(projectName, typescript) {
// vite.config
fs.writeFileSync(
path.join(projectPath, `vite.config.${typescript ? "ts" : "js"}`),
- VITE_CONFIG_JS
+ tailwind ? VITE_CONFIG_JS_TAILWIND : VITE_CONFIG_JS
);
// index.html
@@ -144,16 +178,17 @@ function createVite(projectName, typescript) {
typescript ? INDEX_HTML_TS(projectName) : INDEX_HTML(projectName)
);
- // src/main
- fs.writeFileSync(
- path.join(projectPath, `src/main.${ext}`),
- typescript ? MAIN_TSX : MAIN_JSX
- );
+ // src/main — include index.css import when tailwind is active
+ const mainContent = tailwind
+ ? (typescript ? MAIN_TSX_TAILWIND : MAIN_JSX_TAILWIND)
+ : (typescript ? MAIN_TSX : MAIN_JSX);
+
+ fs.writeFileSync(path.join(projectPath, `src/main.${ext}`), mainContent);
- // src/App
+ // src/App — minimal starter component
fs.writeFileSync(
- path.join(projectPath, `src/App.${ext}`),
- APP_JSX
+ path.join(projectPath, `src/App.${ext}`),
+ APP_JSX
);
// tsconfig if needed
@@ -172,4 +207,4 @@ function createVite(projectName, typescript) {
});
}
-module.exports = createVite;
\ No newline at end of file
+module.exports = createVite;
diff --git a/src/generators/tailwind.js b/src/generators/tailwind.js
new file mode 100644
index 0000000..04fd120
--- /dev/null
+++ b/src/generators/tailwind.js
@@ -0,0 +1,36 @@
+const { execSync } = require("child_process");
+const { writeFile } = require("../utils/files");
+
+// Tailwind v4: PostCSS plugin for CRA (webpack-based)
+const POSTCSS_CONFIG_CRA = `module.exports = {
+ plugins: {
+ '@tailwindcss/postcss': {},
+ },
+}
+`;
+
+// Tailwind v4: single CSS import replaces the three @tailwind directives
+const INDEX_CSS = `@import "tailwindcss";
+`;
+
+function generateTailwind(projectPath, cra) {
+ if (cra) {
+ // CRA uses PostCSS — write the postcss config
+ writeFile(projectPath, "postcss.config.js", POSTCSS_CONFIG_CRA);
+ }
+ // Vite uses @tailwindcss/vite plugin (wired into vite.config) — no postcss config needed
+
+ writeFile(projectPath, "src/index.css", INDEX_CSS);
+
+ const deps = cra
+ ? "tailwindcss @tailwindcss/postcss"
+ : "tailwindcss @tailwindcss/vite";
+
+ execSync(`npm install -D ${deps}`, {
+ cwd: projectPath,
+ stdio: "inherit",
+ shell: true,
+ });
+}
+
+module.exports = generateTailwind;
diff --git a/src/index.js b/src/index.js
index 71b696b..d38bf35 100644
--- a/src/index.js
+++ b/src/index.js
@@ -9,6 +9,7 @@ const createVite = require("./creators/vite");
const cleanCRA = require("./cleaners/cra");
const generateFolders = require("./generators/folders");
+const generateTailwind = require("./generators/tailwind");
const { success, info, error } = require("./utils/logger");
@@ -35,6 +36,7 @@ async function run(projectName, options) {
const useCRA = options.cra;
const typescript = options.ts;
+ const tailwind = options.tailwind;
info(`Using ${useCRA ? "Create React App" : "Vite"}`);
@@ -45,7 +47,7 @@ async function run(projectName, options) {
createCRA(projectName, typescript);
} else {
spinner.stop(); // npm install output needs to show
- createVite(projectName, typescript);
+ createVite(projectName, typescript, tailwind);
}
} catch (e) {
spinner.stop();
@@ -63,11 +65,19 @@ async function run(projectName, options) {
// CRA needs cleanup — Vite is already scaffolded clean
if (useCRA) {
spinner.start("Cleaning starter files...");
- cleanCRA(projectPath, typescript);
+ cleanCRA(projectPath, typescript, tailwind);
spinner.stop();
success("Starter boilerplate removed");
}
+ // Tailwind setup
+ if (tailwind) {
+ spinner.stop();
+ info("Setting up Tailwind CSS...");
+ generateTailwind(projectPath, useCRA);
+ success("Tailwind CSS configured");
+ }
+
// Generate folders
if (options.folders) {
generateFolders(projectPath);
@@ -85,4 +95,4 @@ async function run(projectName, options) {
}
}
-module.exports = run;
\ No newline at end of file
+module.exports = run;