DEV Community

Cover image for What If You Don't Have to Remember Tailwind CSS Classes? Here It Is!
🈚️うひょ🤪
🈚️うひょ🤪

Posted on

What If You Don't Have to Remember Tailwind CSS Classes? Here It Is!

This article is an English translation of an article: Tailwind CSSからクラス名覚えにくさを消したらどうなる? こうなった

Tailwind CSS is a CSS framework with which you utilize predefined CSS classes to compose a design.

I created a new library, Lightwind CSS, which inherits all the goodness of Tailwind CSS but does not require you to memorize class names specific to Tailwind CSS.

https://github.com/uhyo/lightwindcss

Pros and Cons of Tailwind CSS

An example from the official site of Tailwind CSS is quoted below:

<figure class="md:flex bg-gray-100 rounded-xl p-8 md:p-0">
  ...
</figure>
Enter fullscreen mode Exit fullscreen mode

These classes correspond to plain CSS as follows.

  • md:flex@media (min-width: 768px) { display: flex; }
  • bg-gray-100background-color: #F3F4F6;
  • rounded-xlborder-radius: 0.75rem;
  • p-8padding: 2rem;
  • md:p-0@media (min-width: 768px) { padding: 0; }

As you can see, display: flex is shortened into flex. Also, pedding is abbreviated as p.

According to the official site, this way of composing styles is called Utility-First. Utility-First CSS frameworks offer these merits:

  1. You don't have to name classes by yourself. A traditional approach would invent and apply a class name to each component that needs to be styled. With Tailwind CSS, you just use predefined class names whenever you want to apply styles to the DOM.
  2. CSS doesn't grow. With a traditional approach, new CSS is added every time you have a new component to be styled. With Tailwind CSS, you always use the same set of predefined styles so your CSS doesn't grow.
  3. Styles are kept local. A traditional approach would allow using a single class from multiple elements, or writing selectors that indirectly affects descendant elements (like .cls div). Such situations make it hard to predict how a style change affects to your whole app. With Tailwind CSS, your styles are tightly tied to specific DOM elements. Also, Tailwind classes don't have the indirect effects. These characteristics keep the styles local so that making changes to styles are safe.

On the other hand, the author doesn't feel good about how Tailwind CSS isn't CSS while it is based on CSS. As seen in the example above, Tailwind CSS consists of utility classes made by abbreviating and abstracting pure CSS concepts, which means that you have to memorize Tailwind-specific vocabulary. However, this does not mean that you don't have to learn pure CSS in order to use Tailwind CSS. If you don't know how CSS's padding works, you can't understand Tailwind's p-8! Moreover, Tailwind CSS doesn't offer full CSS features through its predefined utility classes. To use modern CSS features, Tailwind CSS isn't sufficient.

Lightwind CSS Lets You Use Plain CSS

Lightwind CSS, the CSS framework created by the author, is designed so that it offers all the merits from Tailwind CSS and also allows use of the plain CSS. For example, you write styles with Lightwind CSS as follows:

<div
  className={css`
    display: flex;
    flex-flow: row nowrap;
    justify-content: center;
  `}
>
  <main
    className={css`
      display: flex;
      flex-flow: column nowrap;
      justify-content: center;
      align-items: center;
    `}
  >
    Hello, world!
  </main>
</div>
Enter fullscreen mode Exit fullscreen mode

Similarly to Tailwind, you attach CSS directly to HTML elements. The css function receives a CSS string and returns corresponding class names. The syntax is actually the same as emotion's css API. This reduces the need of inventing class names similarly to how Tailwind does so. It also helps keep the styles local.

The key feature of Lightwind CSS is the ability to globally optimize CSS for production builds. Lightwind CSS generates one CSS file that contains all the styles in your app. A CSS file that corresponds to the above code would be:

.a {
  display: flex;
  justify-content: center;
}
.b {
  flex-flow: row nowrap;
}
.c {
  flex-flow: column nowrap;
  align-items: center;
}
Enter fullscreen mode Exit fullscreen mode

The markup would be transformed into the following by Lightwind CSS's Babel plugin:

<div className="a b">
  <main className="a c">Hello, world!</main>
</div>
Enter fullscreen mode Exit fullscreen mode

Of note is that Lightwind CSS has optimizes globally; styles from two distinct css are detected and named a, and a is shared by two HTML elements. Usage of css from different files would also be properly optimized. This feature lets us prevent CSS from growing. You may write the same styles in different places, and they are all optimized into one by Lightwind CSS.

In summary, Lightwind CSS takes an opposite approach to Tailwind CSS. While Tailwind CSS first defines the set of class names to be used, Lightwind CSS lets you write arbitrary CSS and then calculate the optimized set of class names from what you wrote. This way, Lightwind CSS still provides you the three merits while not forcing you to remember Tailwind-specific classes.

Comparison to Plain Inline Styles

Lightwind CSS' way of writing styles is close to writing plain inline styles, that is:

<div
  style={{
    display: "flex",
    flexFlow: "row nowrap",
    justifyContent: "center"
  }}
>
  ...
</div>
Enter fullscreen mode Exit fullscreen mode

Actually, the documents of Tailwind CSS discusses it in the section named Why not just use inline styles?, as follows:

  1. Tailwind CSS offers abstracted styles like p-8 or rounded-xl instead of using concrete numbers like padding: 0.75rem; or border-radius: 8px;. This helps you easily build consistent designs.
  2. Media queries cannot be used with inline styles. Tailwind CSS offers media queries-aware classes like md:flex.
  3. Pseudo classes (like :hover or :focus) can neither be used with inline styles. Tailwind CSS also offers classes like hover:bg-purple-700 for this use case.

Lightwind CSS solves 2 and 3 by allowing nested rules:

  <main
    className={css`
      display: flex;
      flex-flow: column nowrap;
      justify-content: center;
      align-items: center;

      &:hover {
        opacity: 0.5;
      }
    `}
  >
    Hello, world!
  </main>
Enter fullscreen mode Exit fullscreen mode

On the other hand, Lightwind CSS excludes the first point -- consistent designs -- from its responsibility, on purpose. This is because we want to provide the experience of writing plain CSS. Building a consistent design system is now your duty.

If you need theming, CSS Variable should be useful. Lightwind CSS might provide a helper to utilize CSS Variables easily in the future.

Notes on Nested Rules

As described above, Lightwind CSS supports nested CSS rules. Please note that it allows you to break the CSS locality, which is one of the goodness of Lightwind CSS that is inherited from Tailwind. For example:

  <main
    className={css`
      display: flex;
      flex-flow: column nowrap;
      justify-content: center;
      align-items: center;

      /* This is not local, affects
       * all the p elements under this!
       */
      p {
        color: red;
      }
    `}
  >
    Hello, world!
  </main>
Enter fullscreen mode Exit fullscreen mode

It may be seen as Lightwind CSS offering more freedom on breaking the locality. Lightwind CSS a light utility so it doesn't restrict a lot of things.

If you want that restriction (I recommend to do so!), you can utilize linters like stylelint to ban the above pattern.

Conclusion

This article introduced Lightwind CSS, a CSS framework that offers the same goodness as Tailwind CSS and lets you write plain CSS instead of Tailwind-specific class names. The key feature of Lightwind CSS is the global optimization to generate one optimized class definitions.

Discussion (1)