Core Concepts

Adding Custom Styles

Best practices for adding your own custom styles to Tailwind.

Often the biggest challenge when working with a framework is figuring out what you’re supposed to do when there’s something you need that the framework doesn’t handle for you.

Tailwind has been designed from the ground up to be extensible and customizable, so that no matter what you’re building you never feel like you’re fighting the framework.

This guide covers topics like customizing your design tokens, how to break out of those constraints when necessary, adding your own custom CSS, and extending the framework with plugins.

Customizing your theme

If you want to change things like your color palette, spacing scale, typography scale, or breakpoints, add your customizations to the theme section of your tailwind.config.js file:

tailwind.config.js

  1. module.exports = {
  2. theme: {
  3. screens: {
  4. sm: '480px',
  5. md: '768px',
  6. lg: '976px',
  7. xl: '1440px',
  8. },
  9. colors: {
  10. 'blue': '#1fb6ff',
  11. 'pink': '#ff49db',
  12. 'orange': '#ff7849',
  13. 'green': '#13ce66',
  14. 'gray-dark': '#273444',
  15. 'gray': '#8492a6',
  16. 'gray-light': '#d3dce6',
  17. },
  18. fontFamily: {
  19. sans: ['Graphik', 'sans-serif'],
  20. serif: ['Merriweather', 'serif'],
  21. },
  22. extend: {
  23. spacing: {
  24. '128': '32rem',
  25. '144': '36rem',
  26. },
  27. borderRadius: {
  28. '4xl': '2rem',
  29. }
  30. }
  31. }
  32. }

Learn more about customizing your theme in the Theme Configuration documentation.


Using arbitrary values

While you can usually build the bulk of a well-crafted design using a constrained set of design tokens, once in a while you need to break out of those constraints to get things pixel-perfect.

When you find yourself really needing something like top: 117px to get a background image in just the right spot, use Tailwind’s square bracket notation to generate a class on the fly with any arbitrary value:

  1. <div class="top-[117px]">
  2. <!-- ... -->
  3. </div>

This is basically like inline styles, with the major benefit that you can combine it with interactive modifiers like hover and responsive modifiers like lg:

  1. <div class="top-[117px] lg:top-[344px]">
  2. <!-- ... -->
  3. </div>

This works for everything in the framework, including things like background colors, font sizes, pseudo-element content, and more:

  1. <div class="bg-[#bada55] text-[22px] before:content-['Festivus']">
  2. <!-- ... -->
  3. </div>

It’s even possible to use the theme function to reference the design tokens in your tailwind.config.js file:

  1. <div class="grid grid-cols-[fit-content(theme(spacing.32))]">
  2. <!-- ... -->
  3. </div>

When using a CSS variable as an arbitrary value, wrapping your variable in var(...) isn’t needed — just providing the actual variable name is enough:

  1. <div class="bg-[--my-color]">
  2. <!-- ... -->
  3. </div>

Arbitrary properties

If you ever need to use a CSS property that Tailwind doesn’t include a utility for out of the box, you can also use square bracket notation to write completely arbitrary CSS:

  1. <div class="[mask-type:luminance]">
  2. <!-- ... -->
  3. </div>

This is really like inline styles, but again with the benefit that you can use modifiers:

  1. <div class="[mask-type:luminance] hover:[mask-type:alpha]">
  2. <!-- ... -->
  3. </div>

This can be useful for things like CSS variables as well, especially when they need to change under different conditions:

  1. <div class="[--scroll-offset:56px] lg:[--scroll-offset:44px]">
  2. <!-- ... -->
  3. </div>

Arbitrary variants

Arbitrary variants are like arbitrary values but for doing on-the-fly selector modification, like you can with built-in pseudo-class variants like hover:{utility} or responsive variants like md:{utility} but using square bracket notation directly in your HTML.

HTML

Generated CSS

  1. <ul role="list">
  2. {#each items as item}
  3. <li class="lg:[&:nth-child(3)]:hover:underline">{item}</li>
  4. {/each}
  5. </ul>

Learn more in the arbitrary variants documentation.

Handling whitespace

When an arbitrary value needs to contain a space, use an underscore (_) instead and Tailwind will automatically convert it to a space at build-time:

  1. <div class="grid grid-cols-[1fr_500px_2fr]">
  2. <!-- ... -->
  3. </div>

In situations where underscores are common but spaces are invalid, Tailwind will preserve the underscore instead of converting it to a space, for example in URLs:

  1. <div class="bg-[url('/what_a_rush.png')]">
  2. <!-- ... -->
  3. </div>

In the rare case that you actually need to use an underscore but it’s ambiguous because a space is valid as well, escape the underscore with a backslash and Tailwind won’t convert it to a space:

  1. <div class="before:content-['hello\_world']">
  2. <!-- ... -->
  3. </div>

If you’re using something like JSX where the backslash is stripped from the rendered HTML, use String.raw() so the backslash isn’t treated as a JavaScript escape character:

  1. <div className={String.raw`before:content-['hello\_world']`}>
  2. <!-- ... -->
  3. </div>

Resolving ambiguities

Many utilities in Tailwind share a common namespace but map to different CSS properties. For example text-lg and text-black both share the text- namespace, but one is for font-size and the other is for color.

When using arbitrary values, Tailwind can generally handle this ambiguity automatically based on the value you pass in:

  1. <!-- Will generate a font-size utility -->
  2. <div class="text-[22px]">...</div>
  3. <!-- Will generate a color utility -->
  4. <div class="text-[#bada55]">...</div>

Sometimes it really is ambiguous though, for example when using CSS variables:

  1. <div class="text-[var(--my-var)]">...</div>

In these situations, you can “hint” the underlying type to Tailwind by adding a CSS data type before the value:

  1. <!-- Will generate a font-size utility -->
  2. <div class="text-[length:var(--my-var)]">...</div>
  3. <!-- Will generate a color utility -->
  4. <div class="text-[color:var(--my-var)]">...</div>

Using CSS and @layer

When you need to add truly custom CSS rules to a Tailwind project, the easiest approach is to just add the custom CSS to your stylesheet:

main.css

  1. @tailwind base;
  2. @tailwind components;
  3. @tailwind utilities;
  4. .my-custom-style {
  5. /* ... */
  6. }

For more power, you can also use the @layer directive to add styles to Tailwind’s base, components, and utilities layers:

main.css

  1. @tailwind base;
  2. @tailwind components;
  3. @tailwind utilities;
  4. @layer components {
  5. .my-custom-style {
  6. /* ... */
  7. }
  8. }

Why does Tailwind group styles into “layers”?

In CSS, the order of the rules in your stylesheet decides which declaration wins when two selectors have the same specificity:

  1. .btn {
  2. background: blue;
  3. /* ... */
  4. }
  5. .bg-black {
  6. background: black;
  7. }

Here, both buttons will be black since .bg-black comes after .btn in the CSS:

  1. <button class="btn bg-black">...</button>
  2. <button class="bg-black btn">...</button>

To manage this, Tailwind organizes the styles it generates into three different “layers” — a concept popularized by ITCSS.

  • The base layer is for things like reset rules or default styles applied to plain HTML elements.
  • The components layer is for class-based styles that you want to be able to override with utilities.
  • The utilities layer is for small, single-purpose classes that should always take precedence over any other styles.

Being explicit about this makes it easier to understand how your styles will interact with each other, and using the @layer directive lets you control the final declaration order while still organizing your actual code in whatever way you like.

The @layer directive helps you control declaration order by automatically relocating your styles to the corresponding @tailwind directive, and also enables features like modifiers and tree-shaking for your own custom CSS.

Adding base styles

If you just want to set some defaults for the page (like the text color, background color, or font family), the easiest option is just adding some classes to the html or body elements:

  1. <!doctype html>
  2. <html lang="en" class="text-gray-900 bg-gray-100 font-serif">
  3. <!-- ... -->
  4. </html>

This keeps your base styling decisions in your markup alongside all of your other styles, instead of hiding them in a separate file.

If you want to add your own default base styles for specific HTML elements, use the @layer directive to add those styles to Tailwind’s base layer:

main.css

  1. @tailwind base;
  2. @tailwind components;
  3. @tailwind utilities;
  4. @layer base {
  5. h1 {
  6. @apply text-2xl;
  7. }
  8. h2 {
  9. @apply text-xl;
  10. }
  11. /* ... */
  12. }

Use the theme function or @apply directive when adding custom base styles if you want to refer to any of the values defined in your theme.

Adding component classes

Use the components layer for any more complicated classes you want to add to your project that you’d still like to be able to override with utility classes.

Traditionally these would be classes like card, btn, badge — that kind of thing.

main.css

  1. @tailwind base;
  2. @tailwind components;
  3. @tailwind utilities;
  4. @layer components {
  5. .card {
  6. background-color: theme('colors.white');
  7. border-radius: theme('borderRadius.lg');
  8. padding: theme('spacing.6');
  9. box-shadow: theme('boxShadow.xl');
  10. }
  11. /* ... */
  12. }

By defining component classes in the components layer, you can still use utility classes to override them when necessary:

  1. <!-- Will look like a card, but with square corners -->
  2. <div class="card rounded-none">
  3. <!-- ... -->
  4. </div>

Using Tailwind you probably don’t need these types of classes as often as you think. Read our guide on Reusing Styles for our recommendations.

The components layer is also a good place to put custom styles for any third-party components you’re using:

main.css

  1. @tailwind base;
  2. @tailwind components;
  3. @tailwind utilities;
  4. @layer components {
  5. .select2-dropdown {
  6. @apply rounded-b-lg shadow-md;
  7. }
  8. .select2-search {
  9. @apply border border-gray-300 rounded;
  10. }
  11. .select2-results__group {
  12. @apply text-lg font-bold text-gray-900;
  13. }
  14. /* ... */
  15. }

Use the theme function or @apply directive when adding custom component styles if you want to refer to any of the values defined in your theme.

Adding custom utilities

Add any of your own custom utility classes to Tailwind’s utilities layer:

main.css

  1. @tailwind base;
  2. @tailwind components;
  3. @tailwind utilities;
  4. @layer utilities {
  5. .content-auto {
  6. content-visibility: auto;
  7. }
  8. }

This can be useful when there’s a CSS feature you’d like to use in your project that Tailwind doesn’t include utilities for out of the box.

Using modifiers with custom CSS

Any custom styles you add to Tailwind with @layer will automatically support Tailwind’s modifier syntax for handling things like hover states, responsive breakpoints, dark mode, and more.

CSS

  1. @tailwind base;
  2. @tailwind components;
  3. @tailwind utilities;
  4. @layer utilities {
  5. .content-auto {
  6. content-visibility: auto;
  7. }
  8. }

HTML

  1. <div class="lg:dark:content-auto">
  2. <!-- ... -->
  3. </div>

Learn more about how these modifiers work in the Hover, Focus, and Other States documentation.

Removing unused custom CSS

Any custom styles you add to the base, components, or utilities layers will only be included in your compiled CSS if those styles are actually used in your HTML.

main.css

  1. @tailwind base;
  2. @tailwind components;
  3. @tailwind utilities;
  4. @layer components {
  5. /* This won't be included in your compiled CSS unless you actually use it */
  6. .card {
  7. /* ... */
  8. }
  9. }

If you want to add some custom CSS that should always be included, add it to your stylesheet without using the @layer directive:

main.css

  1. @tailwind base;
  2. @tailwind components;
  3. /* This will always be included in your compiled CSS */
  4. .card {
  5. /* ... */
  6. }
  7. @tailwind utilities;

Make sure to put your custom styles where they need to go to get the precedence behavior you want. In the example above, we’ve added the .card class before @tailwind utilities to make sure utilities can still override it.

Using multiple CSS files

If you are writing a lot of CSS and organizing it into multiple files, make sure those files are combined into a single stylesheet before processing them with Tailwind, or you’ll see errors about using @layer without the corresponding @tailwind directive.

The easiest way to do this is using the postcss-import plugin:

postcss.config.js

  1. module.exports = {
  2. plugins: {
  3. 'postcss-import': {},
  4. tailwindcss: {},
  5. autoprefixer: {},
  6. }
  7. }

Learn more in our build-time imports documentation.

Layers and per-component CSS

Component frameworks like Vue and Svelte support adding per-component styles within a <style> block that lives in each component file.

While you can use features like @apply and theme inside component styles like this, the @layer directive will not work and you’ll see an error about @layer being used without a matching @tailwind directive:

Don’t use @layer in component styles

Card.svelte

  1. <div>
  2. <slot></slot>
  3. </div>
  4. <style>
  5. /* Won't work because this file is processed in isolation */
  6. @layer components {
  7. div {
  8. background-color: theme('colors.white');
  9. border-radius: theme('borderRadius.lg');
  10. padding: theme('spacing.6');
  11. box-shadow: theme('boxShadow.xl');
  12. }
  13. }
  14. </style>

This is because under-the-hood, frameworks like Vue and Svelte are processing every single <style> block independently, and running your PostCSS plugin chain against each one in isolation.

That means if you have 10 components that each have a <style> block, Tailwind is being run 10 separate times, and each run has zero knowledge about the other runs. Because of this, Tailwind can’t take the styles you define in a @layer and move them to the corresponding @tailwind directive, because as far as Tailwind can tell there is no @tailwind directive to move it to.

One solution to this is to simply not use @layer inside your component styles:

Add your styles without using @layer

Card.svelte

  1. <div>
  2. <slot></slot>
  3. </div>
  4. <style>
  5. div {
  6. background-color: theme('colors.white');
  7. border-radius: theme('borderRadius.lg');
  8. padding: theme('spacing.6');
  9. box-shadow: theme('boxShadow.xl');
  10. }
  11. </style>

You lose the ability to control the precedence of your styles, but unfortunately that’s totally out of our control because of how these tools work.

Our recommendation is that you just don’t use component styles like this at all and instead use Tailwind the way it’s intended to be used — as a single global stylesheet where you use the classes directly in your HTML:

Use Tailwind’s utilities instead of component styles

Card.svelte

  1. <div class="bg-white rounded-lg p-6 shadow-xl">
  2. <slot></slot>
  3. </div>

Writing plugins

You can also add custom styles to your project using Tailwind’s plugin system instead of using a CSS file:

tailwind.config.js

  1. const plugin = require('tailwindcss/plugin')
  2. module.exports = {
  3. // ...
  4. plugins: [
  5. plugin(function ({ addBase, addComponents, addUtilities, theme }) {
  6. addBase({
  7. 'h1': {
  8. fontSize: theme('fontSize.2xl'),
  9. },
  10. 'h2': {
  11. fontSize: theme('fontSize.xl'),
  12. },
  13. })
  14. addComponents({
  15. '.card': {
  16. backgroundColor: theme('colors.white'),
  17. borderRadius: theme('borderRadius.lg'),
  18. padding: theme('spacing.6'),
  19. boxShadow: theme('boxShadow.xl'),
  20. }
  21. })
  22. addUtilities({
  23. '.content-auto': {
  24. contentVisibility: 'auto',
  25. }
  26. })
  27. })
  28. ]
  29. }

Learn more about writing your own plugins in the Plugins documentation.