Most of the tools we use today started with a simple question: can we take a different approach?
Node.js was born when Ryan Dahl asked himself: what if we ran JavaScript on the server? The iPhone came from Steve Jobs wondering: what if we could shrink the iPad? Tailwind CSS emerged because Adam Wathan was fed up with Sass.
A couple of months ago, I started working on my own side project. As always, I reached for Sass. In my opinion, Sass is still a solid choice - it gives developers superpowers like nesting, variables, and mixins. But in the communities I hang out in, the same debate keeps popping up: do we even need Sass anymore?
Not because Tailwind is trendy. Not because “everybody uses it.” But because, as many argue, native CSS has finally caught up.
What really broke me
At the moment this blogpost is being written, the last two–three years have felt like chaos: Russia vs. Ukraine, Israel vs. Palestine, the BLM movement pops up from time to time. Regardless of where you stand politically, one thing should be clear: contests (sports, Eurovision, etc.) and open source communities ought to be bridges between people, not battlegrounds.
That’s why, when I looked at the Sass repository and saw the maintainer decide to mix politics with open source, it broke me. Suddenly, what was once a neutral tool became a statement. For some people, that created a sense of belonging; for others, alienation.
So I asked myself: how can I detach Sass from my projects and rely more on what the web natively gives us? Politics aside, why should we prefer native tools where possible?
- No dependencies on third-party tools. Remember the left-pad fiasco? Entire builds broke because one maintainer pulled the plug. The fewer external tools you depend on, the less fragile your CI/CD becomes.
- Security. In an ecosystem as fragmented as JavaScript, each dependency is a potential weak point. Man-in-the-middle attacks are easier than we’d like to admit - someone literally lost their Bitcoin to a phishing attack like this.
- The “lonely forest” problem. If a tiny library you depend on breaks, it’s suddenly your problem. If something goes wrong in Chrome or Node.js itself, at least you’re not alone - the whole industry rallies behind fixes.
That’s why, for my recent project, I tried to lean as much as possible on native CSS capabilities.
The spark
While working on a client project, I discovered something that surprised me: browsers have supported native CSS nesting since 2023. That’s at least two years ago - which, in programming terms, is basically forever.
So today, we already have nesting and variables built right into CSS. The only thing Sass still seems to hold over us is mixins. But do we really need mixins to write CSS properly? If we think a little outside the box, maybe we can work around that limitation too.
But before we get to solutions, let’s step back and ask the big question:
What’s the problem with Tailwind? Why should we even consider throwing it away?
Why don't I like Tailwind?
Well… for a couple of reasons:
- It’s another dependency. I want my projects to stay as native as possible. The fewer external tools I rely on, the more stable and future-proof the codebase becomes.
- It’s a preprocessor. In the early days of using Tailwind, if you had a conditional class (e.g., based on a variable), sometimes that class would be removed during build. That’s not just confusing - that’s black magic I don’t want in my stack.
- Cognitive overload. Open a component - say, a card - and you’ll often find a mile-long class string. It’s not exactly the best developer experience when you’re just trying to style something. Sure, you could move it into a dedicated class with @apply (inspired by less BTW), but isn’t the whole point of Tailwind not having CSS files? It feels like shooting yourself in the foot.
- Poor debugging workflow. With plain CSS, you can tweak styles directly in the browser dev tools, then copy the changes back into your stylesheet. With Tailwind, your attributes are scattered across massive class lists like debris after a car crash. That’s not a great approach for DX.
- Test fragility. Some teams use CSS selectors in their tests. If your test relies on text-red-500 and you later change it to text-red-400, your test could suddenly break. This can also impact AI-based testing tools like Testim.io, which depend on CSS selectors and whose algorithms may be affected as a result.
Let’s do some code
I still remember how painful it used to be to write and maintain CSS before we had a proper way to encapsulate styles with components. Everything was global, everything leaked, and managing larger projects felt like a nightmare. And when things get horrible, the open-source community usually jumps in to fix it.
That’s how we got tools like LESS, Sass, and later the Compass framework. They gave us structure, shortcuts, and superpowers.
The real turning point came after the “Angular big bang”. Suddenly, CSS needs started to shine - animations, gradients, rounded corners, and eventually Flexbox and Grid. Each new feature raised the bar of what could be done natively, without needing a preprocessor.
But for me, the most underrated feature of them all is CSS variables. Some people dismiss them: “Okay, so I can store my button colors, big deal.” But I see them as just the beginning. Variables open the door to dynamic theming, responsive logic, and design systems that live directly in the browser - no extra tooling required.
Example No. 0 - Nesting CSS
So now, let’s look at the most basic example - a card. Look at this codepen. The css is very hard to understand after you get used to SASS or Less. Now, let’s shape it up 💪🏻 Now that browsers have built in support for nesting CSS we can do it like this.
Look at the css how easy it is to grasp - the title is title and not card-title. If we take it from a programming point of view it is like having a couple of functions that sit in a utils file to a file with a determined logic of its own.
And now… let’s see how css vars are playing a key factor in turning this to a dark mode:
Example No. 1 - Power of CSS variables
When talking with my CTO, Nadav, about this he said “it’s weird no one knows about css variables” but… let’s see a very cool example in action. We already demonstrated a card so let’s do a button.
Here is a small button I created using GPT. Pretty simple. Now, I asked GPT to turn the values into variables - pretty simple. So now… where do I go with this? Let’s ask GPT to have variants of the colors and override the variables.
So, what’s going on here? The success or error variants are global but they still override the colors and you going to say “well.. That’s proper CSS” but it is still cool and we’ll see in the upcoming a more powerful usage of this feature.
Example No. 2 - Responsiveness (part 1)
So now we saw that we have nested variables and we have variables. Bootstrap gave us the responsive UI that today we take for granted. Using classes we could control how the UI will “break” on certain screen sizes: on mobile we’ll have all the elements above each other and on other desktop they will be near each other.
That was ground breaking since websites usually had a site for mobile and site for non mobiles and the backend was just redirecting the user based on certain conditions (remember - the backend used to render the html).
IMO - this kind of lays the foundations to how we think grid and flex box acts. For example, how to think responsive in the new approach i’m having. You can see in the flex box example i’m having a flex-row class that’s break on mobile by turning the direction to columns (BTW, if you using tailwin you need to have flex flex-row and this is bugs me because flex-row is already describing a flex box that need to be in a row. Why have two classes?!)
IMO, having this kind of ability built in by the browser with no extra tools it’s amazing! This is done only by the browser, no extra tools.
Example No. 2 - Responsiveness (part 2)
The second thing when talking about responsiveness is the spacing of elements - outer and inner. For example, in mobile we want to have less spaces, for example 80 percent, so when the user is on mobile we’ll have smaller spaces. This is the codepen with the code but you might want to see the example in full screen
Wrapping up
In the world of web development, things change fast - especially on the frontend. We’ve gone from writing plain CSS (which, by the way, still needs to stay backward compatible - just check out the original Space Jam site still running!) to using preprocessors, build tools, and bundlers that handle our CSS automatically. Today, with browsers’ native CSS features becoming almost magical, it feels like we’ve come full circle.
But what about in the AI world?
Instead of the classic DRY principle (“Don’t Repeat Yourself”), where we extract logic into shared functions and libraries, we now embrace WET — Write Everything Twice. With tools like Gemini, Claude, and Cursor, we can simply prompt them to create what we need, like this:
Generate CSS files for the specified functionality. The files should be placed in a folder named `css/`. There are four files:
1. colors.css – holds color palettes as CSS variables. Palettes: red, yellow, blue, orange, green, pink, purple, azure,
gray, black, white. Each palette has 11 shades, numbered 50 (lightest) through 950 (darkest). The middle range
400–600 represents the balanced/base tones. Variables are named in the format `--color-{name}-{shade}`.
Also, add the next three base colors: #EF7722, #FAA533, #EBEBEB, #0BA6DF
2. colors-dark.css – defines dark mode overrides. Uses a `.dark` selector to override the variables from colors.css.
3. units.css – Defines global scaling and sizing variables. Includes --factor (default value 1),
--mobile-max (the mobile breakpoint), and --unit (the base unit, e.g. 16px). A media query adjusts the --factor to
0.8 when the screen width is below 400px. Generates a consistent set of size variables: --size-2, --size-4, --size-5,
--size-8, --size-10, and then continues in steps of 2 from --size-12 up to --size-80.
4. main.css – imports all the above files. It contains `@import "./colors.css";`, `@import "./colors-dark.css";`, and
`@import "./units.css";`.
As the saying goes, “great artists steal.” Here, we’ve got four files heavily inspired by Tailwind - mainly in their color and unit systems. Beyond the base colors, we can use multiple hues and shades to style buttons, text, and other elements while maintaining visual consistency. Alongside that, the unit variables make it easy to keep spacing uniform across components.
The prompt doesn’t generate additional components like cards, inputs, or buttons - and that’s intentional. I want to avoid what I call “Bootstrapism” - that era when every site using Bootstrap looked exactly the same. Maintaining a distinct visual identity matters, especially now that we’re seeing the same pattern reappear with the rise of frameworks like ShadCN.




