Skip to content

Working with Tailwind

In this section, we are going to dive inside the tailwind.config.js file located at the root of the project. Everything happens there, so we need to give you all the info you'll need to be able to customize it for your needs.

Configuration imports

The tailwind configuration file starts with commonjs (using require instead of import) import statements:

javascript
const colors = require('tailwindcss/colors')
const plugin = require('tailwindcss/plugin')

We are doing 2 things:

  • We first load the colors object from tailwind to be able to access the native colors later in the configuration file
  • We then load the plugin object to be able to use tailwind plugins later in the file.

Dark mode & Content

We then take care of configuring the Dark mode as well as the files to parse, looking for tailwind class names to compile in the final bundle:

javascript
module.exports = {
  darkMode: 'class',
  content: [
    './src/layouts/**/*.{html,js}',
    './src/partials/**/*.{html,js}',
    './src/root/**/*.{html,js}',
  ],
  // ...rest of config
}

We are doing 2 things:

  • We are first telling tailwind to use the class implementation of the dark mode. This means that when the dark mode is toggled, a .dark class is added to the html DOM element, affecting all the application styles and colors. See Tailwind docs for more details about dark mode.
html
<!--Dark mode disabled-->
<html></html>

<!--Dark mode enabled-->
<html class="dark"></html>
  • We then tell tailwind to look for classes to compile inside all files residing inside the src/layouts, src/partials and src/root folders, including files ending with .html and .js extensions. Learn more about content parsing by taking a look at the Tailwind docs.

Extending the base theme

The tailwind configuration file provides a very convenient way of overwriting the default theme and set your own variables. Everything related to theme customization goes inside the extend theme section of the configuration file:

javascript
module.exports = {
  // ...previous config elements
  theme: {
    extend: {
      // The theme configuration goes here.
    }
  }
  // ...rest of config
}

There are several things we can change in there. If you want more details about the tailwind configuration, you can always read the dedicated section of the Tailwind docs. Let's get back to Finity and deep dive in each subsection of the theme configuration.

Breakpoints

In Tailwind CSS, responsive breakpoints are known as screens. Being a mobile first framework, screens are set in an ascending manner, so you always design mobile first. Here is the default Tailwind configuration:

javascript
module.exports = {
  // ...previous config elements
  theme: {
    extend: {
      screens: {
        sm: '640px',
        md: '768px',
        lg: '1024px',
        xl: '1280px',
        xxl: '1536px',
      },
    }
  }
  // ...rest of config
}

While the defaults are pretty satisfying, we though that we could tweak them a little bit to get the most out of it, and to be able to face those very particular edge cases that come up from time to time. Here is Finity's screens configuration:

javascript
module.exports = {
  // ...previous config elements
  theme: {
    extend: {
      screens: {
        xs: { max: '639px' },
        sm: '640px',
        md: '768px',
        lg: '1025px',
        xl: '1280px',
        xxl: '1536px',
        ptablet: {
          raw: '(min-width: 768px) and (max-width: 1024px) and (orientation: portrait)',
        },
        ltablet: {
          raw: '(min-width: 768px) and (max-width: 1024px) and (orientation: landscape)',
        },
      },
    }
  }
  // ...rest of config
}

Let's go through the changes we've made. We've added an xs screen that, opposed to the others that follow an ascending pattern, is capped to 639px to face some very rare edge cases where you'd like to have something only applied on very small screens. We've also decided to push the lg breakpoint to 1025px instead of 1024px to exclude landscape tablets from desktop display, simply because there is not as much room on a landscape tablet as there is on a desktop screen, implying therefore layout adjustments between the two. And finally, we've added two raw screens called ptblet (stands for portrait tablet) and ltablet (stands for landscape tablet) to handle specific layouts rendered in both orientations at maximum design quality.

Colors

Tailwind CSS provides a very advanced and fully customizable color system. If you don't know anything about Tailwind colors, we recommend that you take a look at . the Tailwind docs to learn more about them. In Finity, we use the theme section of the configuration file to configure our own colors, that we will be using in the template. It also means, that you can do the same thing to change all the template colors to match your branding. Here is the color configuration of Finity:

javascript
module.exports = {
  // ...previous config elements
  theme: {
    extend: {
      colors: {
        slate: {
          1000: '#0a101f',
        },
        gray: {
          1000: '#080c14',
        },
        zinc: {
          1000: '#101012',
        },
        neutral: {
          1000: '#080808',
        },
        stone: {
          1000: '#0f0d0c',
        },
        muted: colors.slate,
        primary: colors.indigo,
        info: colors.sky,
        success: colors.teal,
        warning: colors.amber,
        danger: colors.rose,
      },
    }
  }
  // ...rest of config
}

Tailwind CSS provides 22 pre-built colors, each one having 10 different shades, from the lightest one to the darkest one. Let's talk about some of our choices. First, even if the system is pretty complete, we felt it needed an additional really darker shade to enhance dark mode renders. Therefore we decided to add an additional shade to the gray based Tailwind colors.

WARNING

Consider the slate, gray, zinc, neutral and stone colors like base grey-ish backgrounds and tints for your design system and UI elements. Avoid at all costs mixing them as it will affect the consistency of your color system. Instead use one of those consistently throughout your design.

Following the gold rule in the above warning, we've decided to abstract the gray color we want to use for the template inside the muted variable.

javascript
  muted: colors.slate,

That simply is a shortcut to use our slate color through the template without taking the risk of mixing with another tint of gray. Therefore, instead of doing:

html
<span class="text-slate-600"></span>

We now simply write:

html
<span class="text-muted-600"></span>

Yeah, we pretty much know what you're going to say: "Hold on, this is pretty and nice, but how is it going to help me with my development? What benefit is this bringing?".

  • First, you don't take the risk of mixing inconsistent gray shades, having gray in some parts, slate in others, and maybe stone somewhere else. The main gray shade is abstracted in the muted color variable and prevents any potential errors, like when multiple people work on the same project.
  • Second, if you want to change the overall look of your app, you can do it anytime. You just need to switch the color linked to the muted variable, like this:
javascript
  // If you want a slate dominant in your layout
  muted: colors.slate,

  // If you want a gray dominant in your layout
  muted: colors.gray,

  // If you want a zinc dominant in your layout
  muted: colors.zinc,

  // If you want a neutral dominant in your layout
  muted: colors.neutral,

  // If you want a stone dominant in your layout
  muted: colors.stone,

we use the same mechanism to abstract other colors that you might need in a web project, like a primary color or other colors like success and danger that symbolize some application states. You can configure those colors as shortcuts as well:

javascript
  //The template primary color
  primary: colors.indigo,

  //The blue-ish info state
  info: colors.sky,

  //The green-ish success state
  success: colors.teal,

  //The orange-ish warning state
  warning: colors.amber,

  //The red-ish danger state
  danger: colors.rose,

Therefore, instead of using the indigo color, we abstract it to primary:

html
<!--You shouldn't do that-->
<span class="text-indigo-600"></span>

<!--This is the right way-->
<span class="text-primary-600"></span>

You should now have a good understanding of how colors work in Finity. Take a look at how we use them inside the template to get a more precise idea of the possibilities.

Fonts

Tailwind CSS provides a very simple way to abstract your fonts, like we did with colors. If you don't know anything about Tailwind font families, we recommend that you take a look at the Tailwind docs to learn more about how to configure and use them. In Finity, we use the theme section of the configuration file to configure our own fonts. It also means, that you can do the same thing to change all the template fonts to match your branding. Here is the color configuration of Finity:

javascript
module.exports = {
  // ...previous config elements
  theme: {
    extend: {
      fontFamily: {
        sans: ['Roboto Flex', 'sans-serif'],
        heading: ['Inter', 'sans-serif'],
      },
    }
  }
  // ...rest of config
}

We are doing 2 things:

  • We are first telling tailwind to overwrite its base sans font stack and to use Roboto flex instead. You then simply call this font in your templates by using the font-sans class expression.
  • We then add a new utility for headings that is using the Inter font. You then call this font in your templates by using the font-heading class expression.

You could also follow the same pattern if you want to customize the native font-serif and font-mono stacks that Tailwind provides by default. You could add something like this in the configuration above:

javascript
module.exports = {
  // ...previous config elements
  theme: {
    extend: {
      fontFamily: {
        serif: ['Some Font Name', 'serif'],
        mono: ['Some Font Name', 'monospace'],
      },
    }
  }
  // ...rest of config
}

Typography

While we just saw how we can configure fonts in Finity, we also need to start talking about a particular Tailwind plugin, the typography plugin. This plugin is made to facilitate styling of user generated content areas, like blog posts for instance. This controls how native HTML elements without any CSS classes are styled and rendered. This is what Finity uses to style the content inside the blog post page. The section related to typography in the tailwind configuration file is bigger than the others, as you pretty much have to setup everything, as well as the dark mode styles:

javascript
module.exports = {
  // ...previous config elements
  theme: {
    extend: {
      typography: ({ theme }) => ({
        DEFAULT: {
          css: {
            color: theme('colors.muted.600'),
            '[class~="lead"]': {
              color: theme('colors.muted.400'),
            },
            h2: {
              fontFamily: theme('fontFamily.heading'),
              fontWeight: 700,
              color: theme('colors.muted.800'),
            },
            h3: {
              fontFamily: theme('fontFamily.heading'),
              fontWeight: 500,
              color: theme('colors.muted.800'),
            },
            h4: {
              fontFamily: theme('fontFamily.heading'),
              fontWeight: 500,
              fontSize: '1.25em',
              color: theme('colors.muted.800'),
            },
            hr: {
              borderColor: theme('colors.muted.200'),
            },
            li: {
              fontSize: '1.15rem',
              color: theme('colors.muted.600'),
              padding: '0.35rem 0',
            },
            strong: {
              color: theme('colors.muted.800'),
            },
            em: {
              color: theme('colors.muted.500'),
              fontSize: '1.1rem',
              lineHeight: 1,
            },
            blockquote: {
              fontSize: '1.1rem',
              lineHeight: 1.4,
              fontWeight: 500,
              color: theme('colors.muted.500'),
              borderLeftColor: theme('colors.primary.500'),
              background: theme('colors.muted.100'),
              padding: '1.75rem',
            },
            pre: {
              fontFamily: theme('fontFamily.mono'),
            },
            code: {
              fontFamily: theme('fontFamily.mono'),
              background: theme('colors.primary.100'),
              color: theme('colors.primary.500'),
              padding: '0.35rem',
              fontWeight: 600,
              fontSize: '0.95rem !important',
            },
          },
        },
        dark: {
          css: {
            color: theme('colors.muted.400'),
            '[class~="lead"]': {
              color: theme('colors.muted.300'),
            },
            h2: {
              color: theme('colors.muted.100'),
            },
            h3: {
              color: theme('colors.muted.100'),
            },
            h4: {
              color: theme('colors.muted.100'),
            },
            hr: {
              borderColor: theme('colors.muted.800'),
            },
            li: {
              color: theme('colors.muted.400'),
            },
            strong: {
              color: theme('colors.muted.300'),
            },
            em: {
              color: theme('colors.muted.400'),
            },
            blockquote: {
              color: theme('colors.muted.200'),
              background: theme('colors.muted.800'),
            },
          },
        },
      }),
    }
  }
  // ...rest of config
}

See how the typography: ({ theme }) => ({}) function uses the theme variable to gain access to Tailwind classes and utilities you defined earlier in your theme. We are using the css-in-js syntax to style all native HTML elements like h1, h2, blockquote, strong or li. We are then nesting an additional configuration inside the dark property to style for dark mode.

Keyframes

Keyframes are animation definitions written in CSS. While Tailwind provides many of these out-of-the-box, we still needed to add specific ones for our template, using that theme configuration section of the tailwind.config.js file:

javascript
module.exports = {
  // ...previous config elements
  theme: {
    extend: {
      keyframes: {
        indeterminate: {
          '0%': { 'margin-left': '-10%' },
          '100%': { 'margin-left': '100%' },
        },
        placeload: {
          '0%': { 'background-position': '-468px 0' },
          '100%': { 'background-position': '468px 0' },
        },
      },
    }
  }
  // ...rest of config
}

We extend the Tailwind default keyframes by adding two of our own, indeterminate (to animate indeterminate progress bars) and placeload (to animate placeload elements), that we will be using in the animations section.

animations

Like we saw in the previous section, we've added some new keyframes. We're now going to use them in new animation we are also going to add to Tailwind CSS.

javascript
module.exports = {
  // ...previous config elements
  theme: {
    extend: {
      animation: {
        indeterminate: 'indeterminate 1s cubic-bezier(0.4, 0, 0.2, 1) infinite',
        placeload: 'placeload 1s linear infinite forwards',
      },
    }
  }
  // ...rest of config
}

We are declaring the indeterminate and placeload animation that make use of the keyframes we've defined earlier. We also add the animation-timing-function and duration properties, like in traditional CSS. Once our animations are added, we can then simply use them in our HTML template by calling the animate-indeterminate or the animate-placeload classes.

plugins

Like we saw in the very first section of this page, we are first importing the tailwind plugin object to be able to use plugins later in our configuration file.

javascript
const plugin = require('tailwindcss/plugin')

We can then use this plugin definition to actually load our plugins into Tailwind CSS. Keep in mind that those plugins need to be installed via pnpm or it will result in an error when running the project.

javascript
module.exports = {
  // ...previous config elements
  theme: {
    extend: {
      plugins: [
        require('@tailwindcss/typography'),
        require('@tailwindcss/line-clamp'),
        require('@tailwindcss/aspect-ratio'),
        require('@vidstack/player/tailwind.cjs'),
      ]
    }
  }
  // ...rest of config
}

In the sample above, we are loading the typography plugin, which we saw earlier, as well as the line-clamp, aspect ratio and @vidstack plugins.

  • The typography plugin is in charge of styling user generated HTML or markdown content. You can read more about it in the official Tailwind documentaion.
  • The line-clamp plugin is in charge of truncating lines of text, whether on a single or multi-line basis. You can read more about it in the official Tailwind blog.
  • The aspect-ratio plugin is in charge of rendering correct aspect ratios like 1:1, 4:3 or 16:9. You can read more about it in the official Tailwind documentation.
  • The @vidstack plugin is an external plugin in charge of making the @vidstack video player compatible with Tailwind styling. You can read more about it in the official @vidstack player documentation.

Adding plugin utilities

we can also use the plugin object to add flat utilities directly in the configuration file:

javascript
module.exports = {
  // ...previous config elements
  theme: {
    extend: {
      plugins: [
        plugin(function ({ addUtilities }) {
          addUtilities({
            '.slimscroll::-webkit-scrollbar': {
              width: '6px',
            },
            '.slimscroll::-webkit-scrollbar-thumb': {
              borderRadius: '.75rem',
              background: 'rgba(0, 0, 0, 0.1)',
            },
            '.slimscroll-opaque::-webkit-scrollbar-thumb': {
              background: 'rgba(0, 0, 0, 0) !important',
            },
            '.mask': {
              'mask-size': 'contain',
              'mask-repeat': 'no-repeat',
              'mask-position': 'center',
            },
            // ...Other utilities added
          })
        }),
      ]
    }
  }
  // ...rest of config
}

The classes added above will are available everywhere in the project and are also compatible with the Tailwind responsive-variants that rely on the breakpoints you've defined earlier.

Adding plugin components

we can also use the plugin object to add flat CSS components directly in the configuration file:

javascript
module.exports = {
  // ...previous config elements
  theme: {
    extend: {
      plugins: [
        plugin(function ({ addComponents }) {
          addComponents({
            '.placeload': {
              position: 'relative',
              background:
                'linear-gradient(to right, rgb(0 0 0 / 7%) 8%, rgb(0 0 0 / 15%) 18%, rgb(0 0 0 / 7%) 33%)',
              'background-size': '800px 104px',
              color: 'transparent !important',
            },
            '.dark .placeload': {
              background:
                'linear-gradient(to right, rgb(255 255 255 / 15%) 8%, rgb(255 255 255 / 24%) 18%, rgb(255 255 255 / 15%) 33%)',
            },
          })
        }),
      ]
    }
  }
  // ...rest of config
}

The classes added above will are available everywhere in the project. They work like traditional CSS and you could as well chose not adding them here but directly in the postcss files located in src/root/postcss instead. Also, keep in mind that unlike the plugin utility classes we've added earlier, these component utilities are do not generate responsive-variants.

Complete configuration

That's it! Now that we've been through the entire Tailwind configuration file, you should have a solid understanding of what's happening in there, how to make changes to adapt it to your context and to get the most out of it. Here is the complete configuration that we've been through:

javascript
const colors = require('tailwindcss/colors')
const plugin = require('tailwindcss/plugin')

module.exports = {
  darkMode: 'class',
  content: [
    './src/layouts/**/*.{html,js}',
    './src/partials/**/*.{html,js}',
    './src/root/**/*.{html,js}',
  ],
  theme: {
    extend: {
      screens: {
        xs: { max: '639px' },
        sm: '640px',
        md: '768px',
        lg: '1025px',
        xl: '1280px',
        xxl: '1536px',
        ptablet: {
          raw: '(min-width: 768px) and (max-width: 1024px) and (orientation: portrait)',
        },
        ltablet: {
          raw: '(min-width: 768px) and (max-width: 1024px) and (orientation: landscape)',
        },
      },
      colors: {
        slate: {
          1000: '#0a101f',
        },
        gray: {
          1000: '#080c14',
        },
        zinc: {
          1000: '#101012',
        },
        neutral: {
          1000: '#080808',
        },
        stone: {
          1000: '#0f0d0c',
        },
        muted: colors.slate,
        primary: colors.indigo,
        info: colors.sky,
        success: colors.teal,
        warning: colors.amber,
        danger: colors.rose,
      },
      fontFamily: {
        sans: ['Roboto Flex', 'sans-serif'],
        heading: ['Inter', 'sans-serif'],
      },
      typography: ({ theme }) => ({
        DEFAULT: {
          css: {
            color: theme('colors.muted.600'),
            '[class~="lead"]': {
              color: theme('colors.muted.400'),
            },
            h2: {
              fontFamily: theme('fontFamily.heading'),
              fontWeight: 700,
              color: theme('colors.muted.800'),
            },
            h3: {
              fontFamily: theme('fontFamily.heading'),
              fontWeight: 500,
              color: theme('colors.muted.800'),
            },
            h4: {
              fontFamily: theme('fontFamily.heading'),
              fontWeight: 500,
              fontSize: '1.25em',
              color: theme('colors.muted.800'),
            },
            hr: {
              borderColor: theme('colors.muted.200'),
            },
            li: {
              fontSize: '1.15rem',
              color: theme('colors.muted.600'),
              padding: '0.35rem 0',
            },
            strong: {
              color: theme('colors.muted.800'),
            },
            em: {
              color: theme('colors.muted.500'),
              fontSize: '1.1rem',
              lineHeight: 1,
            },
            blockquote: {
              fontSize: '1.1rem',
              lineHeight: 1.4,
              fontWeight: 500,
              color: theme('colors.muted.500'),
              borderLeftColor: theme('colors.primary.500'),
              background: theme('colors.muted.100'),
              padding: '1.75rem',
            },
            pre: {
              fontFamily: theme('fontFamily.mono'),
            },
            code: {
              fontFamily: theme('fontFamily.mono'),
              background: theme('colors.primary.100'),
              color: theme('colors.primary.500'),
              padding: '0.35rem',
              fontWeight: 600,
              fontSize: '0.95rem !important',
            },
          },
        },
        dark: {
          css: {
            color: theme('colors.muted.400'),
            '[class~="lead"]': {
              color: theme('colors.muted.300'),
            },
            h2: {
              color: theme('colors.muted.100'),
            },
            h3: {
              color: theme('colors.muted.100'),
            },
            h4: {
              color: theme('colors.muted.100'),
            },
            hr: {
              borderColor: theme('colors.muted.800'),
            },
            li: {
              color: theme('colors.muted.400'),
            },
            strong: {
              color: theme('colors.muted.300'),
            },
            em: {
              color: theme('colors.muted.400'),
            },
            blockquote: {
              color: theme('colors.muted.200'),
              background: theme('colors.muted.800'),
            },
          },
        },
      }),
      keyframes: {
        indeterminate: {
          '0%': { 'margin-left': '-10%' },
          '100%': { 'margin-left': '100%' },
        },
        placeload: {
          '0%': { 'background-position': '-468px 0' },
          '100%': { 'background-position': '468px 0' },
        },
        stroke: {
          '100%': { 'stroke-dashoffset': '0' },
        },
        scale: {
          0: { transform: 'scale(0)', opacity: 0 },
          '100%': { transform: 'scale(1)', opacity: 1 },
        },
        kenburns: {
          0: { 'background-size': '120% auto' },
          '50%': { 'background-size': '100% auto' },
          '100%': { 'background-size': '120% auto' },
        },
        stripemove: {
          to: {
            transform: 'rotateX(45deg) translate(0) skew(-60deg)',
          },
        },
      },
      animation: {
        indeterminate: 'indeterminate 1s cubic-bezier(0.4, 0, 0.2, 1) infinite',
        placeload: 'placeload 1s linear infinite forwards',
        circle: 'stroke 1.2s cubic-bezier(0.65, 0, 0.45, 1) forwards',
        check: 'stroke 0.9s cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards',
        scale: 'scale 0.5s linear 0.5s forwards',
        kenburns: 'kenburns 12s linear infinite reverse',
        wheel: 'spin 3s linear infinite',
        rotation: 'spin 1.5s infinite linear reverse',
        stripemove: 'stripemove 0.5s linear infinite',
      },
    },
  },
  variants: {
    extend: {},
  },
  plugins: [
    require('@tailwindcss/typography'),
    require('@tailwindcss/line-clamp'),
    require('@tailwindcss/aspect-ratio'),
    require('@vidstack/player/tailwind.cjs'),
    plugin(function ({ addUtilities }) {
      addUtilities({
        '.slimscroll::-webkit-scrollbar': {
          width: '6px',
        },
        '.slimscroll::-webkit-scrollbar-thumb': {
          borderRadius: '.75rem',
          background: 'rgba(0, 0, 0, 0.1)',
        },
        '.slimscroll-opaque::-webkit-scrollbar-thumb': {
          background: 'rgba(0, 0, 0, 0) !important',
        },
        '.mask': {
          'mask-size': 'contain',
          'mask-repeat': 'no-repeat',
          'mask-position': 'center',
        },
        '.mask-hex': {
          'mask-image':
            "url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE4MiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNjQuNzg2IDE4MS40Yy05LjE5NiAwLTIwLjA2My02LjY4Ny0yNS4wNzktMTQuMjFMMy43NjIgMTA1LjMzYy01LjAxNi04LjM2LTUuMDE2LTIwLjkgMC0yOS4yNTlsMzUuOTQ1LTYxLjg2QzQ0LjcyMyA1Ljg1MSA1NS41OSAwIDY0Ljc4NiAwaDcxLjA1NWM5LjE5NiAwIDIwLjA2MyA2LjY4OCAyNS4wNzkgMTQuMjExbDM1Ljk0NSA2MS44NmM0LjE4IDguMzYgNC4xOCAyMC44OTkgMCAyOS4yNThsLTM1Ljk0NSA2MS44NmMtNC4xOCA4LjM2LTE1Ljg4MyAxNC4yMTEtMjUuMDc5IDE0LjIxMUg2NC43ODZ6Ii8+PC9zdmc+')",
        },
        '.mask-hexed': {
          'mask-image':
            "url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTgyIiBoZWlnaHQ9IjIwMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNLjMgNjUuNDg2YzAtOS4xOTYgNi42ODctMjAuMDYzIDE0LjIxMS0yNS4wNzhsNjEuODYtMzUuOTQ2YzguMzYtNS4wMTYgMjAuODk5LTUuMDE2IDI5LjI1OCAwbDYxLjg2IDM1Ljk0NmM4LjM2IDUuMDE1IDE0LjIxMSAxNS44ODIgMTQuMjExIDI1LjA3OHY3MS4wNTVjMCA5LjE5Ni02LjY4NyAyMC4wNjMtMTQuMjExIDI1LjA3OWwtNjEuODYgMzUuOTQ1Yy04LjM2IDQuMTgtMjAuODk5IDQuMTgtMjkuMjU4IDBsLTYxLjg2LTM1Ljk0NUM2LjE1MSAxNTcuNDQuMyAxNDUuNzM3LjMgMTM2LjU0VjY1LjQ4NnoiLz48L3N2Zz4=')",
        },
        '.mask-deca': {
          'mask-image':
            "url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkyIiBoZWlnaHQ9IjIwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNOTYgMGw1OC43NzkgMTkuMDk4IDM2LjMyNyA1MHY2MS44MDRsLTM2LjMyNyA1MEw5NiAyMDBsLTU4Ljc3OS0xOS4wOTgtMzYuMzI3LTUwVjY5LjA5OGwzNi4zMjctNTB6IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=')",
        },
        '.mask-blob': {
          'mask-image':
            "url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMTAwIDBDMjAgMCAwIDIwIDAgMTAwczIwIDEwMCAxMDAgMTAwIDEwMC0yMCAxMDAtMTAwUzE4MCAwIDEwMCAweiIvPjwvc3ZnPg==')",
        },
        '.mask-diamond': {
          'mask-image':
            "url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMTAwIDBsMTAwIDEwMC0xMDAgMTAwTDAgMTAweiIgZmlsbC1ydWxlPSJldmVub2RkIi8+PC9zdmc+')",
        },
      })
    }),
    plugin(function ({ addComponents }) {
      addComponents({
        '.placeload': {
          position: 'relative',
          background:
            'linear-gradient(to right, rgb(0 0 0 / 7%) 8%, rgb(0 0 0 / 15%) 18%, rgb(0 0 0 / 7%) 33%)',
          'background-size': '800px 104px',
          color: 'transparent !important',
        },
        '.dark .placeload': {
          background:
            'linear-gradient(to right, rgb(255 255 255 / 15%) 8%, rgb(255 255 255 / 24%) 18%, rgb(255 255 255 / 15%) 33%)',
        },
      })
    }),
  ],
}

All Rights Reserved