Rose Pine
← All posts

Devlog

Feb 14, 2026

Devs should have problems

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:

  • @babel/parser

  • @babel/traverse

  • postcss

This allowed me to:

  1. Parse React files into AST

  2. Detect style={{}} objects

  3. Extract properties safely

  4. Convert them to Tailwind utilities

  5. Remove the style attribute

  6. 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 🙂

By Vyeos