Frontend development is constantly evolving to match user expectations and experience, as well as simplify and speed up the development processes and reduce the release cycles.
New frameworks, paradigms, and architectures are created in this constant search for solutions, some of which disrupt the industry or drastically change the way developers work.
In the last few years, a new set of frameworks with radically different concepts drew the attention of frontend developers. These frameworks are now extremely popular in the frontend world, and you’re likely already familiar with them: Tailwind CSS and Pollen.
In this article, we’ll discuss their advantages, disadvantages, implementation paradigms, and more, so that you can make an informed decision on which framework to use in your next project.
Tailwind CSS disrupted the way we design websites and web applications. Though controversial at first, it has captured the love of many frontend developers because of its simplicity and consistency, and because it provides a faster way to write CSS.
But what problem with CSS was Tailwind trying to address? Browser compatibility. Although general CSS has improved over the years, websites will look different in different browsers if we’re not careful.
In addition, we have user-generated problems, magic numbers, class names that spiral out of control, and the use of !important
as a means to override lousy design.
Tailwind CSS simplifies this by extracting CSS, standardizing units, colors, and properties, and guaranteeing cross-browser compatibility.
It also provides a collection of classes representing each possible style configuration, and devs can use those classes as building blocks for their designs.
The best part? It works! Working with Tailwind CSS is wonderful, though it comes with some downsides that we will discuss next.
Tailwind CSS isn’t perfect, and one of the most obvious flaws in the framework is how ugly it looks when implemented. Even its creator asks developers to “suppress the urge to retch long enough to give it a chance”.
But what exactly is the problem? Let’s see an example of a simple button built with Tailwind CSS:
<button type="button" class="inline-flex items-center px-2.5 py-1.5 border border-transparent text-xs font-medium rounded shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> Button text </button>
There’s no denying that simple objects like buttons can become overly verbose and ugly to read, but if you give Tailwind a chance, its simplicity will overcome its syntax.
Every frontend developer is familiar with CSS, and, through experience, devs start learning countless properties and values to achieve the desired looks on a site.
When switching to Tailwind, even though most class names are intuitive, devs must learn them, ranging from simple things like adding padding and margins to working with selectors and nested properties.
For example, if we want to center a text using CSS, we would apply the following:
text-align: center;
Here’s the code we’d use in Tailwind CSS:
text-center
The name text-center
makes total sense, but it is something we must relearn. Things can also get a bit more complicated when we move on to selectors.
Let’s look at the following CSS example:
<style> .my-button { background-color: #4CAF50; /* Green */ border: none; color: white; } .my-button:hover { background-color: white; color: black; border: 2px solid #4CAF50; } </style> <button class="my-button" />
And a similar implementation using Tailwind:
<button class="bg-green-600 hover:bg-white border-0 hover:border-2 text-white hover:text-black hover:border-green-600" />
Selectors work totally differently than they do with CSS — they prepend a class name, and it becomes difficult to understand the difference between one state and another.
As with CSS classes, states are grouped together; with Tailwind CSS, each class is adjusted independently from the rest and could work in pretty much any order.
What if we have multiple buttons on the screen, all with the same margins, paddings, and properties, but with different colors? We would have to duplicate several classes throughout our HTML code, and, in case we need to change padding, code p-2
to p-4
. It becomes a tedious task.
What’s Tailwind’s solution to that problem? Work with a classical CSS approach, define your classes, and set your properties. They even provide a way to use Tailwind’s classes as definitions for your own custom classes, which looks like the following:
<style> .my-button { @apply bg-green-600 border-0 text-white; } .my-button:hover { @apply bg-white border-2 text-black border-green-600; } </style> <button class="my-button" />
And that’s a real soup. It’s not Tailwind, it’s definitely not CSS, and it doesn’t look clean.
Enter Pollen, which was built to preserve the natural beauty of Tailwind CSS while addressing some of the problems we’ve discussed. It takes a totally different approach by using CSS variables.
The idea behind Tailwind CSS is glorious. The problem is its implementation. Breaking its relationship with CSS stripped it from the familiarity and power of CSS. Pollen, on the contrary, relies on CSS and provides a set of predefined CSS variables for standardization.
Let’s take a look into our simple button example, now using Pollen:
<style> .my-button { background-color: var(--color-green-500) border: none; color: white; } .my-button:hover { background-color: white; color: black; border: 2px solid var(--color-green-500); } </style> <button class="my-button" />
The syntax looks familiar, and we still conserve some of the benefits of using Tailwind CSS.
So, is Pollen simply an evolution and better than Tailwind? Not necessarily! Pollen comes with its own pros and cons, too.
Each project normally names CSS classes differently, even in the same company, and that will translate into being difficult for developers to find out if classes are in use, if new ones need to be added for the new component being built, and in some cases, even breaking design because we modified or deleted a class that was being used in more places than we initially thought.
This fundamental problem was addressed in Tailwind CSS by standardizing class names. In Pollen, however, user-generated classes are the norm. Thus, Pollen is equally vulnerable to the problem as regular CSS.
It is not terribly wrong, and good engineering practice can help to address it. However, based on my experience, every project eventually lands in CSS chaos.
Even though Pollen can be extended with add-ons, it is not as complete as Tailwind. One of my favorite examples is CSS grids. Grids can be difficult to understand and implement, and Tailwind CSS does a great job abstracting some of the complexity with classes like grid-col-1
, grid-col-3, grid-span-2,
etc.
With Pollen, you are pretty much on your own.
Pollen is a relatively new framework, while Tailwind CSS has been battle-tested in thousands of different projects
Although it conceptually looks great, how it performs in the field will determine if it is a framework that is here to stay.
This is not a fair comparison, but let’s look at the stats at the moment of writing for both projects.
Tailwind CSS | Pollen | |
Stars | ~50k | 500 |
Forks | 2.3k | 9 |
Commits | ~4k | 196 |
Contributors | 203 | 5 |
Tailwind CSS is clearly dominating (for now), but Pollen is growing rapidly, and if developers get to know the project and love it, it has the potential to catch up to Tailwind CSS quickly.
Both Tailwind CSS and Pollen aim to prioritize consistency, standardization, and composability, though with two totally different implementations.
There is no right answer to the question, “Which framework is best?”, as each comes with strengths and weaknesses, but I’d like to give my personal opinion on the matter.
My personal preference is to use Tailwind CSS. I believe that the benefits outweigh the problems, and, after working on multiple projects with Tailwind, I really enjoy the experience.
Why do I prefer Tailwind CSS over Pollen? Because Pollen is no different from what I was already doing with CSS.
When I work on a new CSS-based project, I’d first create a set of CSS variables to reflect basic patterns from the design system, so, in a way, I was already working similarly to the way Pollen does, though perhaps with fewer options. The same will likely apply to many of you.
Pollen didn’t do anything radically different. It just offers more to what we were already doing in CSS. Tailwind CSS, on the contrary, introduced a different approach. It abstracts complexity and, ignoring the ugly class syntax, is fantastic to work with.
Because of that, my heart still belongs to Tailwind CSS.
Which is your favorite framework? Let me know in the comments. I’d love to hear your thoughts.
Thanks for reading!
As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.
Modernize how you debug web and mobile apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.