DEV Community

Cover image for Simplify TailwindCSS with TailwindBox
jn
jn

Posted on • Edited on

Simplify TailwindCSS with TailwindBox

TailwindCSS is powerful, but it can be hard to read. Writing conditional styles can also be a hassle. So I decided to create a lightweight and simple library to solve this problem.

TailwindBox

GitHub logo vectordxy / tailwindbox

Easily manage TailwindCSS styles with simple object-like structures

banner

🔧 Installation

Install TailwindBox via npm or yarn:

npm install tailwindbox
# or
yarn add tailwindbox
Enter fullscreen mode Exit fullscreen mode

🚀 Usage

Here's a quick example of how to use TailwindBox:

import { tw } from "tailwindbox";

function App() {
  const isDarkMode = true;

  const styles = tw({
    base: "p-4 rounded-lg shadow-md",
    dark: { if: isDarkMode, classes: "bg-gray-800 text-white" },
    light: { if: !isDarkMode, classes: "bg-white text-black" },
  });

  return <div className={styles}>Hello, TailwindBox!</div>;
}

export default App;
Enter fullscreen mode Exit fullscreen mode
  • base: Always applies the base styles (p-4 rounded-lg shadow-md).
  • dark: Applies bg-gray-800 text-white only if isDarkMode is true.
  • light: Applies bg-white text-black only if isDarkMode is false.

🌟 Features

  • Object-Like Structure: Define TailwindCSS styles in an object-based…

TailwindBox makes your TailwindCSS code cleaner and easier to manage. Here's a quick example of how to use TailwindBox:

import { useState } from "react";
import { tw } from "tailwindbox";

function App() {
  const [isOpened, setIsOpened] = useState(false);

  const styles = tw({
    base: "pt-2 text-center font-light",
    open: { if: isOpened, classes: "inline-block border-t p-7" },
    closed: { if: !isOpened, classes: "hidden" },
  });

  return (
    <div>
      <button
        onClick={() => setIsOpened((prev) => !prev)}>
        Toggle Content
      </button>
      <div className={styles}>Hello World!</div>
    </div>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode
  • base: Always applies the base styles (pt-2 text-center font-light).
  • open: Applies inline-block border-t p-7 only if isOpened is true.
  • closed: Applies hidden only if isOpened is false.

Features

  • Object-Like Structure: Define TailwindCSS styles in an object-based format.
  • Conditional Classes: Apply classes dynamically based on your application's state.
  • Improved Readability: Simplify long and complex class strings.
  • Lightweight: A minimal library designed for TailwindCSS users.

It will be a great help when you develop with TailwindCSS. Try it out, and feel free to share your feedback anytime!

Top comments (16)

Collapse
 
valeriavg profile image
Valeria

That reminds me a lot of cx from @emotion/css, which boils down to:

<div className={cx(base, isOpened && opened)} />
Enter fullscreen mode Exit fullscreen mode

Any particular reason you decided to have an object structure instead?

Collapse
 
vectordxy profile image
jn

Thank you for the question. Actually, my library was influenced by emotion, as I thought its structure was well-designed. The main reason I created this library was to improve projects that were already built with TailwindCSS. I noticed that conditional className strings often became too long and complicated, and I wanted a way to simplify that.

If you’re starting a project, using emotion or another styling solution might be a better choice. However, for projects already written with TailwindCSS, I believe this library makes className simpler.

I chose the object structure simply because it felt familiar to me. I found the object-based approach clear. This approach started from my own perspective, but I’m constantly learning through feedback! I appreciate your valuable thoughts.

Collapse
 
valeriavg profile image
Valeria

Thank you for your response! It’s possible to use emotion/cx with tailwind (string css classes defined elsewhere) as well, but you’re right it’s much easier to use something else of tailwind. Would you say your library can be used as a bridge while refactoring as well or is the purpose to simplify use of tailwind and promote it?

Thread Thread
 
vectordxy profile image
jn

This library was primarily designed as a refactoring tool. It’s not intended to promote the use of TailwindCSS but rather to simplify className in projects that already use TailwindCSS. I truly appreciate your interest. Thank you!

Thread Thread
 
michi profile image
Michael Z • Edited

@moopet regarding

That means if you have something like mx-${isWide ? 5 : 3} neither mx-3 nor mx-5 will be included

In this case, I recommend writing out the class names: isWide ? mx-5 : mx-3.
Not just for tailwind, but for anything in programming, otherwise you'll have a hard time finding them later as the code base grows (grep test).

Thread Thread
 
moopet profile image
Ben Sinclair

I agree, but it's an example of how Tailwind (and lots of other modern things with build steps) does magic in the background, where if you don't know about it, you're going to fall into traps.

Collapse
 
moopet profile image
Ben Sinclair

The example you give (switching between dark and light themes) is exactly why tailwind is a bad solution for styling things in the first place.
But I thought they had prefixes for that sort of thing, like dark:text-black or similar? I mean, obviously they'd only support two options whereas your solution could cope with many - or straight CSS could cope with unlimited options.

Collapse
 
vectordxy profile image
jn

Thank you for your feedback! I created this library to solve issues in React. When conditional logic is applied, className often become difficult to read. For example, something like className='${isOpened ? "inline-block" : "hidden"} text-center font-light border-t p-7 m-5 text-sm'} is too long. The main goal is to make it easier to manage styles based on variables in React components. The dark mode example wasn’t the best choice to explain this. I’ll update the example to better reflect the library’s purpose.

As a junior React developer, I don’t have much experience with plain CSS, so I didn’t consider using it. Your feedback has helped me understand how to approach CSS better. I truly appreciate your insights. Thank you!

Collapse
 
moopet profile image
Ben Sinclair

Tailwind is the source of a lot of those problems as far as I'm concerned.

If you use conditional logic like that, it can... not always work.

Tailwind does something crazy, it basically searches the js files for things that look like its known class names and then only includes those in the build. That means if you have something like mx-${isWide ? 5 : 3} neither mx-3 nor mx-5 will be included, but if your page is discussing the merits of the Mazda mx-5, then you're in luck and it will work... half the time.

My issue is that people are having to make hacks upon hacks just to get back to the point where CSS already solved things :/

Thread Thread
 
asmyshlyaev177 profile image
Alex

My issue is that people are having to make hacks upon hacks just to get back to the point where CSS already solved things :/

Probably they do, not only with CSS.

The whole mess with CSS started because an attempt to find some good system to organize styles, we've got css-in-js, it was better because code and styles were kept together and no need to jump between files, tailwind goes in the same direction, it's easier when styles closer to hmtl markup.

Thread Thread
 
best_codes profile image
Best Codes

Tailwind CSS was actually created to solve problems with consistency, which it does quite well.

Here's an interesting article which discusses that (a bit):
adamwathan.me/css-utility-classes-...

Thread Thread
 
asmyshlyaev177 profile image
Alex

Consistency is another side of the same issue.
Article is good, I read it some time ago.

Personally I like plain css, now when nesting natively supported, or css modules. Tailwind nice for layout related things, for don't repeat flex with centering all over again, and few more.

But it brings some old problems back, since it's gathering classes from source code, it works differently on dev mode and after bundling, in result works in dev, but messed up after bundling it.

Thread Thread
 
best_codes profile image
Best Codes

In my experience with Tailwind, it hasn't ever applied CSS differently in development than in a build. If it did, it probably worked in a build but not in development.

For me, it's awesome; it helps with reusability and consistency, responsiveness, accessibility, and a lot more.

It's a lot easier to write

<div class="flex flex-col w-11/12 max-w-md bg-white rounded-lg overflow-hidden">
        <div class="flex-1 m-2 p-5 bg-green-500 text-white transition-transform duration-300 motion-safe:animate-bounce">
            Box
        </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Than the same HTML with

        .container {
            display: flex;
            flex-direction: column;
            width: 90%;
            max-width: 600px;
            background-color: #fff;
            border-radius: 8px;
            overflow: hidden;
        }

        .box {
            flex: 1;
            margin: 10px;
            padding: 20px;
            background-color: #4CAF50;
            color: white;
            transition: transform 0.3s;
        }

        @media (prefers-reduced-motion: no-preference) {
            .box {
                animation: bounce 1s infinite;
            }
        }

        @keyframes bounce {
            0%, 100% {
                transform: translateY(0);
            }
            50% {
                transform: translateY(-20px);
            }
        }

        @media (max-width: 600px) {
            .container {
                flex-direction: row;
                flex-wrap: wrap;
            }
        }
Enter fullscreen mode Exit fullscreen mode

Or course there are times when I just use the style prop instead, but tailwind is generally a lot faster.

 
moopet profile image
Ben Sinclair

But I think CSS-in-JS is the wrong direction, as well :P

Collapse
 
asmyshlyaev177 profile image
Alex • Edited

Interesting idea, I will give it a try later.

Curious, what will happen with this styles object during minification?

With tailwind can also use classic classNames like this

<div className="header">...
Enter fullscreen mode Exit fullscreen mode
// in css file
.header {
  @apply min-h-[35vh] md:min-h-[25vh] flex flex-col items-center mb-8;
Enter fullscreen mode Exit fullscreen mode
Collapse
 
vectordxy profile image
jn

Thank you for the question!

To be honest, I haven’t thought much about how minification would affect this. I assume it depends on the build tool being used (e.g., Vite, Webpack). However, JavaScript object keys are typically not changed during minification, so the styles object is likely to remain unchanged. I’ll make sure to look into this further!

@apply is also a great solution. However, this library was specifically designed to make conditional styling simpler. In particular, it can be helpful in situations where styles need to be applied dynamically based on variables.

Thank you again for your valuable feedback. It gives me a great opportunity to learn and grow!