Tailwind CSS from Zero to Hero - Dark Mode, JIT & More!

Tailwind CSS from Zero to Hero - Dark Mode, JIT & More!
Author
Vaibhav Khulbe
Related tags on daily.dev

🎯

We covered so much about Tailwind CSS beginning from the first part of the series where we barely knew anything about it to the last part in which we upgraded our skills to make a production-ready website. But there’s always some room to learn more. Of course, there will be new additions every day but I believe there are some topics that need special mention so that you are ready from ‘Zero’ and become a complete ‘Hero’ in Tailwind.

There is a bonus! Who doesn’t like it?

Here’s what we will be covering in this article:

  • Using with Preprocessors
  • Adding a dark mode
  • Functions and directives
  • Configuring styles based on a design system
  • New JIT mode
  • Extending default breakpoints
  • Adding accessibility support

Let’s get to know about these real quick!

Using with Preprocessors

While you won’t need another CSS preprocessor or a different CSS framework when you are already styling your elements with Tailwind CSS, still if by any change your project requires to use a preprocessor like SASS, LESS or Stylus, then this may help.

Note that if you are interested in using PostCSS, then it’s better to check out the previous articles in this series.

Before we even begin writing some code we need to have an additional build step. If you followed the previous demos, we used Autoprefixer which is a recommended builder which is commonly used. If you are using some other frameworks like Vue or Nextjs, you can read on the docs page how to configure this. Here’s a list of caveats for each preprocessor that you have to know before using it with Tailwind CSS.

Working with SASS

Suppose you have an HTML element with a custom class called chat-box with the following code:


.chat-box {
  @apply text-blue-500;
}

Here, you want to override the default blue color shade of the text from 400 to 500 (as depicted here). Now to do this you add !important at the end of the @apply rule like:


.alert {
  @apply bg-red-500 !important;
}

But here a problem arises. It won’t work. Whenever you want to use !important with @apply, it needs to be interpolated as #{!important}. So our code now becomes:


.alert {
  @apply bg-red-500 #{!important};
}

Working with Less

When you are using Tailwind’s @screen directive, you can’t next it like:


.myElement {
  @apply text-lg;

  @screen sm {
    @apply text-sm;
  }
}

Instead, we need to use a media query that goes with the theme() function so that we can add our preferred screen size.


.card {
  @apply text-lg;

  @media (min-width: theme('screens.sm')) {
    @apply text-sm;
  }
}

Working with Stylus

With Stylus running with Tailwind, the @apply directive doesn’t work unless you wrap the entire CSS rule with @css.

Here’s an example:


@css {
  .alert {
    @apply bg-red-500 p-6 border-black rounded-md;
  }
}

One quick note to add here is that by using @css the core Stylus features won’t work. Instead of using @apply, you can use theme() to write the actual CSS properties in Stylus. So, the above example now becomes:


.card {
  background-color: theme(colors.red.500');
  padding: theme('spacing.6');
  border-radius: theme(borderRadius.md');
}

Learn more on this topic on the official docs here.

Adding a dark mode

While other CSS frameworks and libraries can make the dark mode process fairly complex, Tailwind CSS knows beforehand that adding a dark theme variant is needed these days. To make the entire process seem like a jiffy, it includes a dark variant that lets you style your entire website differently when dark mode is activated. But of course, it doesn’t come enabled by default because of some file size considerations. But you can manually enable it by adding the darkMode option in your Tailwind config file (tailwind.config.js) and setting it to media:


// tailwind.config.js

module.exports = {
  .
  .
  .
  darkMode: 'media',
}

Now, we are ready to write some dark mode utility classes. Let’s assume we have the following HTML template for our regular interface (without dark mode):


<div class="bg-red-500 p-6 border-black rounded-md shadow-sm text-center">
    <h3 class="text-white text-xl font-bold uppercase">Attention users!</h1>
    <p class="text-gray-100 mt-6">This alert means that you can no longer avail this offer. The offer ended this weekeed. </p>
</div>

Then, to add a dark variant, we simply have to add the dark: prefix as:


<div class="bg-red-500 dark:bg-red-900 p-6 border-black rounded-md shadow-sm text-center">
    <h3 class="text-white dark:text-gray-50 text-xl font-bold uppercase">Attention users!</h1>
    <p class="text-gray-100 dark:text-gray-200 mt-6">This alert means that you can no longer avail this offer. The offer ended this weekeed. </p>
</div>

You can play around with the above code and see how it looks by toggling dark/light mode in your operating system here.

Learn more on this topic on the official docs here.

Functions and Directives

There are many custom functions and directives in Tailwind but here, we will discuss those we didn’t discuss before.

    1. @layer: this tells Tailwind in which category a certain set of your custom styles belongs. Some of them are base, components, and utilities. They are your base         styles of commonly used elements like headings, custom utilities of your components, and the different states like hover or active.


@layer base {
  h1 {
    @apply text-3xl;
  }
}

@layer components {
  .primary-btn {
    @apply bg-yellow-600 hover:bg-yellow-800 text-black font-bold py-4 px-6 shadow-md;
  }
}

@layer utilities {
  @variants hover {
    .padding-large {
      padding: 30px;
    }
  }
}

    2. @variants: this is really helpful whenever we want to generate different states of our own utilities. We use them with the @variants directive.

        One thing to note here is that all these variants will be generated in the order we specify them.


@variants focus hover {
  .border-small {
    border: 1px solid #30485e;
  }
  .border-medium-dashed {
    border: 7px dashed #30485e;
  }
}

In the above example, Tailwind will generate three CSS styles as:


.border-small {
  border: 1px solid #30485e;
}

.focus\:border-small:focus {
  border: 1px solid #30485e;
}

.hover\:border-small:hover {
  border: 1px solid #30485e;
}

.border-medium-dashed {
  border: 7px dashed #30485e;
}

.focus\:border-medium-dashed:focus {
  border: 7px dashed #30485e;
}

.hover\:border-medium-dashed:hover {
  border: 7px dashed #30485e;
}

    3. @responsive: what if you want to have a separate responsive custom utility class of your own classes? This can be done using the @responsive directive as:


@responsive {
  .border-solid {
    border: 10px solid #30485e;
  }
}

So now you don’t need to worry about the default breakpoints Tailwind comes with, the great thing is that it will generate the same class of each of the sm, md, lg and xl classes.

    4. @screen: let’s say we have the default sm breakpoint at 640px and you need to code some custom utilities for this breakpoint. Instead of writing a new media query         for each of the classes, you can simply go with this:


@screen sm {
 .border-solid {
    border: 10px solid #30485e;
  }
}

You just have to use the @screen directive followed by the breakpoint name. Here’s another example:


@screen sm {
 .border-solid {
    border: 10px solid #30485e;
  }
}

@screen md {
 .border-solid-large {
    padding: 5rem;  
    border: 10px solid #30485e;
  }
}

@screen lg {
 .border-dotted-large {
    padding: 10rem;
    border: 10px solid #30485e;
  }
}

Learn more on this topic on the official docs here.

Configuring styles based on a design system

When you install Tailwind with the command line, it comes with a config file at the root of your project where you can add any customized styles as per the need of a design system you are following. All you have to do is to specify what you like to change from the Tailwind’s default configuration.

Let’s see what you can configure here:

    1. Theme: here define all the color swatches you will need, the border-radius for elements, and everything else:


// tailwind.config.js

module.exports = {
  theme: {
    colors: {
      red: colors.coolGray,
      indigo: colors.lightBlue,
      pink: colors.rose,
    },
    fontFamily: {
      sans: ['Open Sans', 'sans-serif'],
      serif: ['Roboto Slab', 'serif'],
    },
    extend: {
        borderRadius: {
        ‘3xl’: ‘3rem’,
        '5xl': '5rem',
      }
    }
  }
}

    2. Variants: here you add all the different variants for each of your utilities:


// tailwind.config.js

module.exports = {
  variants: {
      extend: {
      backgroundColor: ['focus-visible'],
    }
  },
}

    3. Prefix: this lets you add your own custom prefix for all the Tailwind utilities. This comes in handy whenever you have naming conflicts with other libraries or        frameworks.

So, if you are tired of using the usual bg-red-400 and want it to be xyz-bg-red-400, then all you have to do to add the xyz- prefix is:


/ tailwind.config.js

module.exports = {
  prefix: 'xyz-',
}

Then you can use it in your HTML as:


<div class="xyz-bg-red-400 hover:xyz-bg-red-600"></div>

    4. Plugins: here, to get some extra utilities or custom variants, you can add Tailwind’s official plugins.


// tailwind.config.js

module.exports = {
  plugins: [
    require('@tailwindcss/forms'),
    require('@tailwindcss/typography'),
],
}

The good part is you can even write your own plugins, read how to do this here.

Learn more on this topic on the official docs here.

New JIT mode

Please note that this feature is currently (while writing this blog) in preview. This means that there might be some changes later.

The JIT or Just In Time mode comes from Tailwind version 2.1 and above. It’s a new compiler that generates all the CSS styles behind the scenes on-demand when you write HTML templates.

Previously, it used to generate all the classes in advance at build time. Now with JIT enabled, Tailwind will only do this on-demand.

Here’s why you should start using it:

  • Fast build time: even big projects can now be compiled in ~800ms.
  • Better development performance: when you have a heavy project with multiple technologies this helps as the browser doesn’t have to parse lots of pre-generated CSS code.
  • Every variant is enabled: this comes like a bonus out-of-the-box. So all of your variants like active, focus, disabled etc. are enabled to work instantly since with JIT all the code will generate on-demand.

Enable JIT mode

In the config JS file, you just need to set the mode option to jit as:


// tailwind.config.js

 module.exports = {
     mode: 'jit',
     // ...
 }

Next, make sure you configure the purge option to include all the template file paths if you have not done so already. If you are new to this make sure to read more about it in one of the previous articles in this series. When you are done with this, and run the development server, Tailwind CSS will now switch to the JIT mode generating styles on-demand.

Learn more on this topic on the official docs here.

Extending default breakpoints

You might have been in situations when you have to change the webpage layout targeting a specific device breakpoint that’s not in the default breakpoints provided by the library. Still, you can extend the breakpoints by adding larger or smaller breakpoint values. You need to add the extend key to add a larger breakpoint:


// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      screens: {
        '3xl': '1600px',
        ‘4xl’: ‘2000px’
      },
    },
  },
  variants: {},
  plugins: [],
}

For a smaller breakpoint, we can’t use extend, instead, we override the entire screens key as:


// tailwind.config.js
const defaultTheme = require('tailwindcss/defaultTheme')

module.exports = {
  theme: {
    screens: {
      'xs': '475px',
      ‘xxs’: ‘360px’,
      ...defaultTheme.screens,
    },
  },
  variants: {},
  plugins: [],
}

Learn more on this topic on the official docs here.

Adding accessibility support

If you are a developer working on the front-end of an application, accessibility should be your top priority. Thankfully, Tailwind CSS comes with some utilities to improve this with screen readers.

The two utility classes provided are sr-only and not-sr-only.

  • sr-only: this is used to hide any HTML element visually but not from screen readers. It can be translated as “screen readers only” so only they will be allowed to read.

// index.html

<button type=”submit”>
   <span class="sr-only">I’m visible to screen readers only</span>
</button>

  • not-sr-only: this is the opposite of sr-only and makes an element visible to both sighted users as well as screen readers. One useful example of this is to enable it when a user clicks the “Skip to content” or “Move to top” links:

<a href="#" class="sr-only focus:not-sr-only">
  Move to top 
</a>

Learn more on this topic on the official docs here.

Conclusion

In this bonus article, you got to know all the other important topics and features Tailwind CSS provides. We started with how to integrate it with SASS, LESS, and other popular preprocessors, then we added a dark mode theme, and learned important directives like @variant.

Then we did a deep dive into the config file to configure it to match a design system and got to know how useful the new JIT mode can be, extending the default breakpoints by adding smaller and larger ones, and we finished off by adding great accessibility support.

By this, we have concluded our entire Tailwind CSS series. It’s like a gem that is shining really great in the front-end world and I recommend it to use wherever you can!

I hope I was able to provide you with all the important aspects to go from ‘Zero’ to ‘Hero’ in Tailwind.

See you later in my next article!

Why not level up your reading with

Stay up-to-date with the latest developer news every time you open a new tab.

Read more