DEV Community

Cover image for How using Tailwind enhances your coding skills
Fernando González Tostado
Fernando González Tostado

Posted on • Edited on

How using Tailwind enhances your coding skills

Recently, there has been a lot of debate among developers in social media about the effectiveness of Tailwind in improving developer experience (DX) when styling components. Some argue that it is just inline styling using classes, and that it can clutter your HTML markup with long class names.

scooby doo phantom tailwind meme

long tailwind class names meme

Ok, enough memes. Let's get serious.

Personally, I have used Tailwind in my recent personal projects. My initial experience was mixed. I found that I was repeating styling for many elements and my coding speed was slower due to the unfamiliarity with the naming conventions. I often found myself needing to look up basic selectors for margins, padding, and widths.

A simple styled button would end up like this, with a very verbose className.

<button
 className="absolute top-0 bottom-0 -right-14
     flex items-center justify-center border-0 p-0 text-center
     hover:no-underline hover:outline-none focus:no-underline focus:outline-none"
 type="button"
/>
Enter fullscreen mode Exit fullscreen mode

Using BEM and scss this button would look like this:

<button className="carousel__control--next"/>
Enter fullscreen mode Exit fullscreen mode

Obviously the carousel__control–next class won't add any style by itself. I had to have that selector somewhere in my source:

// somewhere in my scss
// src/styles/components/_carousel.scss


.carousel__control--next {
 top: 0;
 bottom: 0;
 right: -14px;
 border: 0;
 padding: 0;
 text-align: center;
 &:hover {
   text-decoration: none;
   outline: none;
 }
 &:focus {
   text-decoration: none;
   outline: none;
 }
}
Enter fullscreen mode Exit fullscreen mode

I found that using Tailwind did not save me lines of code, but rather added them directly to my elements. Although I was starting to remember the most commonly used class names, it did not improve my developer experience as it only eliminated the need for .scss files.

I was accustomed to separating styling into a separate directory, following an atomic design approach, which made my code appear cleaner and helped me organize my components and their respective styles.

In conclusion, I did not see a significant reduction in lines of code when using Tailwind, but I believed that the real benefit of using it would be the ability to memorize the most frequently used classes. This would help me add extra details to my components and make them look more polished. For example:

// using Tailwind and BEM
<button className="carousel__control--next mx-auto my-12 md:my-4 w-full md:w-1/2"/>
Enter fullscreen mode Exit fullscreen mode

Wow, that's the best of both worlds, isn't it?!

NO, I was completely missing the big picture.

While you can mix selectors and tailwind classname, or add Tailwind classnames to your selectors using @apply in your files to write mixed CSS:

.carousel__control--next {
 // the rest of the styles
 @apply mx-auto my-12 md:my-4 w-full md:w-1/2;
}
Enter fullscreen mode Exit fullscreen mode

It is not really meant to be used for removing HTML markup from your elements, but for overriding styles from a third party library. For example, this Menu.Item component from Semantic UI React:

// overriding a semantic ui button
import { Menu } from 'semantic-ui-react';


<Menu.Item />


// overriding the Item styles with tailwind


.ui.secondary.pointing.menu .item {
 @apply mx-auto my-12 md:my-4 w-full md:w-1/2;
}
Enter fullscreen mode Exit fullscreen mode

In using Tailwind, the benefit is not just in saving lines of code, but also in not having to spend time thinking about class names and jumping between files. By focusing on reusability, which is closely tied to important coding best practices, Tailwind can help enhance our coding skills.

How does Tailwind could actually push us to improve my coding skills?

Have you heard of the SOLID principles? It is basically a set of rules that should help us to make our code more functional, maintainable and robust.

What do SOLID principles have to do with Tailwind?

Both strive for reusability and separation of concerns.

*SOLID and Tailwind
*

Let's start with the first and most important SOLID principle: the Single Responsibility Principle, which encourages us as developers to write components that do only one thing.

Let's say we have a button component, we intend that button to be used for firing the onClick event, or if it's a form button, a submit event, etc:

// a button that accepts onClick, className, type and children props


const Button = ({ onClick, className, type, children, ...props }) => (
 <button
   onClick={onClick}
   className={className}
   type={type}
   {...props}
 >
   {children}
 </button>
);
Enter fullscreen mode Exit fullscreen mode

Now, let's say that we want this button to be our main button for our app. Here's where Tailwind comes in.

const Button = ({ onClick, className, type, children, ...props }) => (
 <button
   onClick={onClick}
   className={`mx-auto my-12 md:my-4 py-2 px-4
   w-full md:w-1/2 bg-blue-500 font-bold text-white
   flex items-center justify-center rounded
   hover:bg-secondary-blue hover:no-underline hover:outline-none focus:no-underline focus:outline-none
   ${className}`}
   type={type}
   {...props}
 >
   {children}
 </button>
);
Enter fullscreen mode Exit fullscreen mode

We now add the fixed classes and accept a className prop, for adapting our button for the specific needs, saving us a lot of lines of code. Also, if we would need to update the styling of the button, we could safely do it, without fear of breaking other components in the way, which is something that could easily happen if we were updating the css file names directly.

By creating a reusable Button component, we might be also forced to think of making use of the atomic design folder structure, where we group our components from generic to specific.

src
├── components
│   ├── atoms
│   │   ├── button.tsx
│   │   ├── link.tsx
│   │   ├── index.ts
│   │   └── ...
│   ├── molecules
│   │   ├── index.ts
│   │   └── ...
│   ├── organisms
│   │   ├── index.ts
│   │   └── ...
│   ├── templates
│   │   ├── index.ts
│   │   └── ...
Enter fullscreen mode Exit fullscreen mode

And When using atomic design we indirectly strive for the Open/Closed Principle, where now that we have smaller items, we can use them as part of a bigger one.

For example, in this Page component:

// this a page component
import { Container, Header } from 'src/components/atoms'
import { ProductSearchbar } from 'src/components/molecules'
import { Table } from 'sr/components/organisms'


return (
 <Container className='text-center'>
   <Header className='3xl'>Hello!</Header>
   <Header className='xl'>I'm a page component</Header>
   <ProductSearchbar className='w-1/2 md:w-1/3 mx-auto' />
   <Table data={tableItems} className='w-full' />
 </Container>
)
Enter fullscreen mode Exit fullscreen mode

Finally, the third SOLID principle involved: the Dependency Inversion Principle, which basically tells us that the components should not care of the implementation details, a higher in the tree level component should handle that:

import { Button } from 'src/components/atoms'
import { Container } from 'src/components/atoms'
import { SomeComponentThatUsesData } from 'src/components/organisms'


export const SomeComponentWithButtons = () => {
 const [data, setData] = useState(null);


 const handleFetchData = async () => {
   // fetch data
   const res = await fetch('https://jsonplaceholder.typicode.com/todos/1');
   const data = await res.json();
   setData(data);
 }


 const handleDeleteData = () => {
   // delete data
   setData(null);
 }


 return (
   <Container className='my-2 md:my-12 w-full md:w-1/2'>
     <Button onClick={handleFetchData} className='bg-blue-500'>Fetch data</Button>
     <Button onClick={handleDeleteData} className='bg-red-500'>Delete data</Button>
     <SomeComponentThatUsesData data={data} />
   </Container>
 )
}
Enter fullscreen mode Exit fullscreen mode

The SomeComponentWithButtons is a component that handles some data in state, that data is then passed to SomeComponentThatUsesData.

As you can see, the buttons don't care about the implementation details, they just know that they receive an onClick prop that is triggered when they are clicked. Whatever is done —and how— it's irrelevant for the button.

The same goes for the SomeComponentThatUsesData component. It can be reused in several other parts of our code without caring how the data received is obtained, while the data is of the type that it requires.

Conclusion

Tailwind is a powerful tool that can greatly enhance your coding skills if you fully embrace and adhere to its core principles. Its emphasis on reusable elements not only promotes clean, maintainable code but also can help enforce SOLID principles without you even realizing it.

However, the ultimate level of power that Tailwind can bring to your development process ultimately depends on your willingness to fully utilize and commit to its recommended practices.

While I have only touched on the basics of the SOLID principles, they can be as complex as you want to make them. By integrating them with Tailwind, you have a great opportunity to fully understand and implement them in your development process. Understanding and utilizing SOLID principles can greatly advance your career as a developer.

Sources

Top comments (2)

Collapse
 
veggigit profile image
veggi

thx, useful tips.👏

Collapse
 
esponges profile image
Fernando González Tostado

Glad you liked it :).