Making the Website
A few days ago, I rebuilt my portfolio using Astro (as I wanted to try it). But as a beginner mistake, I used AI from start to finish so it used vanilla CSS. I didn't consider it a problem then because I didn't had to change it now or later.
The problem came when I decided to add blogs to my website. Blogs are dynamic, they have to be fetched from a cms and have to be displayed. Astro is not very good at dynamic content. That forced me to move everything to NextJS. I did most of the migrations myself as it was just to add a
export default function page(props...){
...
return(...)
}
Now that I shifted to NextJS, I wanted to use tailwind instead of vanilla CSS (devs having random thoughts). I didn't want to manually rewrite all my CSS into Tailwind classes. So instead of rewriting styles -
I built a migration tool.
The Problem
The issue I had:
- External CSS files
- Class-based styles
- Hundreds of simple declarations like:
display: flex;
justify-content: center;
font-weight: bold;
margin: 16px;
Rewriting all of that into:
className="flex justify-center font-bold m-4"
was not happening manually. This gave me an idea of creating a vanilla to TW migration tool. Starting my chat with AI, the first thing it said was to not use regex and use AST instead.
Using regex, I just search and replace CSS patterns. But JSX is not plain HTML. It breaks instantly:
<div style={{ display: "flex" }} />
If you regex that, you’ll:
- Break syntax
- Miss nested cases
- Destroy formatting
- Fail on conditional classNames
So it showed me the correct route.
⸻
The Solution: AST-Based Transformation
Instead of treating code as text, AST treats it as structured data. I used:
This allowed me to:
- Parse React files into AST
- Detect style={{}} objects
- Extract properties safely
- Convert them to Tailwind utilities
- Remove the style attribute
- Merge with existing className
All without breaking JSX.
⸻
Example
Before
<div style={{ display: "flex", justifyContent: "center" }} />
After
<div className="flex justify-center" />
It also supports:
- Basic external CSS
- Simple class selectors
- className merging
- Dry-run mode
⸻
The Tool
I packaged it as: css-to-tailwind-react
You can install it with:
npm install css-to-tailwind-react
pnpm add css-to-tailwind-react
bun add css-to-tailwind-react
Run:
npx css-to-tailwind-react ./src
pnpm dlx css-to-tailwind-react ./src
bunx css-to-tailwind-react ./src
Current Limitations (in Beta)
It currently skips:
- Media queries
- Pseudo selectors (:hover, :focus)
- Nested selectors
- SCSS
- Animations
- CSS variables
Right now it handles simple and common CSS rules reliably.
⸻
The Reason
This wasn’t just about avoiding typing. This was about solving a problem that I faced. Many devs are confused about what should their next project be. Instead of searching the web and asking advice to others they can just create a solution to a problem they face.
⸻
Open Source
It’s open source and available here:
GitHub:
http://github.com/vyeos/css-to-tailwind-react
It’s also published on npm.
If you try it on a real project, I’d love feedback.
Especially:
- Edge cases it breaks
- CSS patterns it misses
- Tailwind mappings that feel wrong
⸻
What’s Next
Planned improvements:
- Media query support
- Pseudo selector conversion
- CSS Modules support
- Smarter Tailwind scale matching
- Interactive CLI mode
This is still beta.
But it works surprisingly well for simple migrations.
If you test it, let me know what breaks 🙂
Top comments (0)