Tailwind CSS is a popular modern CSS framework known for its utility-first approach. Unlike component-based frameworks like Bootstrap, Tailwind provides a collection of low-level utility classes that you combine to achieve your desired styles. This approach offers greater flexibility and granular control over your styles. It also boasts excellent build optimizations, including generating only the classes used in your project. While Tailwind offers built-in customization options, this article explores various methods for creating custom classes, with a focus on maintaining a positive developer experience. ## Tailwind CSS: Beyond the Basics - Crafting Custom Classes Tailwind CSS has a lot of classes, but there is some time you find yourself the need to use a class that are not in the default package, so what you do? There are a lot of ways to create custom classes, described in [docs](https://tailwindcss.com/docs/adding-custom-styles). I am gonna go though all of them, but be aware my favorite is the last one- By creating a plugin, so if you want, just skip to here ## Arbitrary values-properties-variants Tailwind is built for really hard customization, and that’s so true, you can literaly create any class just combining some prefix or sufix and the compiler will generate for you the classes for build. So, Why we avoid this approach when dealing to customization? The answer is because this goes agains the reusability concept of Tailwind, and if you use more than one that that arbitrary value, I recommend to create a custom utility for it. <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/76b33e7a-bd3f-4940-aabd-e3232034fc7b" name="Arbitrary property Tailwind example" alt="code example of tailwind arbitrary using a color of background" /> <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/9994a2a8-a1df-4be8-813a-759d7ea15e64" name="Build file " alt="Build file showing the arbitrary property became other class" /> ## Customizing the theme If you want to change the properties already build in, like the font-size or color, it’s perfect and really easy to do just customizing the Tailwind theme file. <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/3fcdaf65-9624-4966-8ccd-49aa74daf8c1" name="tailwind.config.ts" alt="Creating a class my-blue in Tailwind file" /> <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/61559e23-be18-43ea-a0a5-04dc0fbca834" name="Code example" alt="Using the custom classes created in a div" /> <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/810e5dfc-70f1-4505-88df-bc1a25c8653d" name="Build file- This may looks weird, but is the same blue I configured, but just with some Tailwind optimization." alt="Build file showing that Tailwind handle better using theme" /> The problem is when you need to create a custom component class, like you repeat that same classes and that became redundant and hard to read. <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/5920d701-17f9-476e-8df4-c5c3024c4594" name="Example of reduntant classes" alt="File showing a lot of container with same classes" /> Isn’t better having just one class like `hello-world-container`? Yes. ## @layer and @apply directive There is a way to add custom classes using some Tailwind directives in CSS files. Like this: <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/53c6e419-42b5-472e-a321-1af95569e58b" name="Example of reduntant classes" alt="File showing a lot of container with same classes" /> You can literaly put any CSS valid property just like the way you would do in normal CSS, but noticed I put the classes inside some directives, why? Tailwind is composed by layers that can be overwritten, eg. You have a base style for your button but you want to change it’s color just in one component, to make that you will need to overwrite it, in normal CSS you will probably use !important, and trust me in my experience if you had use !important, probabily there is something wrong with your code. So use layers with wisdom. I recommend using: - [@layer base](https://tailwindcss.com/docs/adding-custom-styles#adding-base-styles) for tag styles, like I used in the example for tag `code`. - [@layer components](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes) for classes that has more than one style like the `hello-world-container` which has **display: flex;flex-direction:column;justify-content:center;background-color:purple;padding:1rem**. Most customization you will need is gonna be made here. - [@layer utilities](https://tailwindcss.com/docs/adding-custom-styles#adding-custom-utilities) is the highest priority, so use carefully. I recommend using just for one single property like **transparent:background-color:transparent**. This will mostly be used for classes that do not exist in default Tailwind. Well, so we learned about the @layer directive, but what is that @apply in the example? This is a way to use your Tailwind token in the styling file. For build does not matter if you use the **hello-world-container** or **hello-world-container-2**, both produces the same result, so @apply helps your code readbility. Using directives may seen the best way to create custom classes in Tailwind, but as I said in the beginning of the article, this is not my favorite way. I prefer much more creating a custom plugin. ## Why I do not like Tailwind directives like @layer and @apply Fist I need to say why I love the experience of working with Taiwlind insted other CSS framework similar like Bootstrap. Tailwind has been investing not just in optimizations and fast files, but in development experience too. Tailwind has a very nice VSCode plugin called [Tailwind Intellisense](https://tailwindcss.com/docs/editor-setup#intelli-sense-for-vs-code) I personally do not recommend you use Tailwind without this plugin. One thing it changes is when you hover a class, it shows what is that CSS equivalent, is literally required for me and I do not see me not using it. <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/9b5e1959-3c80-4589-867c-47b0fdf40e48" name="Tailwind VSCode Intellisense showcase" alt="Print showing when hovering the class has a popup showing CSS equivalent" /> You know, when you create custom classes, you need Tailwind Intellisense to recognize it, but I never found a way to make recognize directives like @layer. When creating custom classes like that, you do not have the hover preview :( This is unacceptable, specially because custom classes do not exist in Tailwind Docs, is the most important classes to have a register and a easy way to check the CSS equivalent. So going though Github Issues and making a lot of testing. I probably have the best solution to create custom classes in Tailwind. ## Create custom plugins As you can see there are a lot of ways to customize you styles in Tailwind, but there is superior way to customize and still get the Tailwind Intellisense working for us, it’s called 🪽 Plugins 🪽. What is and how we implement? <Blockquote cite="https://en.wikipedia.org/wiki/Plug-in_(computing)"> is a software component that adds a specific feature to an existing computer program </Blockquote> So in [Tailwind](https://tailwindcss.com/docs/plugins) is not diffent, plugins are a way to modify the base of Tailwind, you can do a lot of it, but in this article I just gonna show how you can use to create custom classes styles. Every Tailwind project has a **tailwind.config.js** or **tailwind.config.ts** which has all of Tailwind’s configuration, including the **theme** from earlier, but what we want is the **plugins** <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/27b51eae-d5a0-4c14-81cf-19c97aa76398" name="tailwind.config.ts" alt="tailwind.config.ts file" /> You will need to use a function from Tailwind called `plugin`. I highly recommend using Typescript, for its typing features, which helps a lot, but of course you can use Vanila Javascript too. <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/e42b46ba-2710-4575-81b4-6ad8e864e30f" name="Plugin function" alt="Plugin function" /> In it’s parameters you put what you want to create. For this article, we want a component, so we use the addComponent param. <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/bee18037-70d5-435f-810f-fa6876a62629" name="Plugin function add components" alt="Plugin function add components" /> Now just add the class name and the styles like CSS or (CSS Style Declation)[https://www.w3.org/TR/cssom/#the-cssstyledeclaration-interface] (backgroundColor insted background-color). <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/5a6db547-3f58-4eca-98ce-c5564885fbd6" name="Custom class made by plugin example" alt="Custom class made by plugin example" /> And there you go, now you have the hovering effec from Tailwind Intellisense :) <Img src="https://github.com/fescherer/fennec-tales-blog/assets/62115215/688da377-c59e-4cd2-ba24-b02499d4da25" name="Class in action" alt="Print showing when hovering the custom class made by plugin, showing the equivalent CSS" /> ## More dev experience Here's an additional tip to elevate your Tailwind development experience: consider using the [eslint-plugin-tailwindcss package](https://www.npmjs.com/package/eslint-plugin-tailwindcss). This ESLint plugin offers various features to help you write cleaner and more maintainable Tailwind code. ## Summary This article explored various methods for creating custom classes in Tailwind CSS, emphasizing the importance of a positive development experience. By leveraging Tailwind plugins, you can create custom styles while maintaining IntelliSense support and overall code clarity. This approach empowers you to extend Tailwind's capabilities while keeping your development workflow efficient and enjoyable. Thanks for reading see you in the next article, have a nice day 😊
In this article I will talk about some strategies you can use to create themes on Tailwind, a way to write CSS in utility classes, like if you want to say `display: flex`, you can just give a *classname* of `flex`. Of course this is a very simple example, but you can do so much more and everything is customizable. [Tailwind CSS](https://tailwindcss.com) as the docs says: <Blockquote cite="https://eslint.org/docs/latest/use/getting-started"> A utility-first CSS framework packed with classes like `flex`, `pt-4`, `text-center` and `rotate-90` that can be composed to build any design, directly in your markup. </Blockquote> ### What is Tailwind CSS To understand Tailwind CSS is good to know how it was created. Well, the framework was created by [Adam Wathan](https://twitter.com/adamwathan) because he wanted to use a [Bootstrap](https://getbootstrap.com) kinda syntax with [Less processor](https://lesscss.org), as you may know Bootstrap has build in classes for `buttons`, `forms`, etc. Tailwind in other hand has just utility classes, like `flex`, `backgrounds`, `paddings`, `margins`, etc. That's why in the first versions of Tailwind, it has some of the classes like Bootstrap, but from time on Adam switched to more generic classes. He also needed to change the processor to PostCSS, but you can find the whole history at [OfferZen Origins's channel](https://www.youtube.com/watch?v=1x7HlvSfW6s). For me, frameworks that's uses utility classes are the best ones, it can be strange at first but the productivity grows so much using this strategy that I cannot stop using. ### Popularity For popularity Tailwind is one of the favorite CSS Framework out there. You can see this in [State of CSS Survey](https://2023.stateofcss.com/en-US/css-frameworks): <Img src="https://github.com/fescherer/blog/assets/62115215/23055cb4-026e-4f25-b287-e3556251faf3" name="State of CSS" source="https://2023.stateofcss.com/en-US/css-frameworks" alt="Survey from State of CSS showing the is one of most used technologies for styling" /> They have a lot of interesting data. I suggest you to take look and see for yourself. ### How to use As said before, Tailwind uses utility classes, so you will add them into your html as so: ```html {1-2} <div className="m-4 flex items-center justify-center"> <span>Item 1</span> <span>Item 2</span> </div> ``` When I started coding, the company used [Angular](https://angular.io) with kinda of utility classes. We a had a big style sheet containing all the most used classes like `padding` or `margin` and them, if we would need some more specific style, we create a class just for it. I grow up and for my experience, don't do that, try to stay just with utility classes, it way more easy to maintain. You can find the list of utility classes available [in the docs](https://tailwindcss.com/docs/utility-first). ## What is theme Theme is a set of characteristics that build an idea. I can be applied to an environment to give the feeling of that characteristics. So you can made a party using a Halloween theme, so everyone and the party probably will have characteristics from Halloween. In design is the same thing, we can made certain [rules that build in a theme](https://www.uxpin.com/studio/blog/design-system-theming), like font sizes, spaces, images and colors. This can be used for accessibility advantages or just to made a design some unique like a special version for christmas. It became very popular for all designs to have at least a version called `dark` with more darker colors and a `light` with, of course, lighter colors. So it is very important to know how you can handle this. ## Strategies for theming in Tailwind In this tutorial, I will show only themes that change colors, that's because I think changing the font-size or spaces can be tricky and not so great in the final product. Tailwind already has a [theme build in support](https://tailwindcss.com/docs/dark-mode) but I think is not ideal mostly if you want to add more than just dark and light theme. So that's why I gonna show you some strategies you can use to add more themes for Tailwind CSS. ### Using external libraries There a lot of libs created by the community to solve the problem of multiple themes in Tailwind. Some examples would be: - [Tailwind CSS Theme Variants](https://github.com/JNavith/tailwindcss-theme-variants); - [Tailwind Themer](https://github.com/RyanClementsHax/tailwindcss-themer); - [TW Colors](https://github.com/L-Blondy/tw-colors); - Many more... All have a very similar concept and way to solve this by enable to create themes on `Tailwind.config.ts`, as an example of configuring three themes dark, light and forest using [TW Colors](https://github.com/L-Blondy/tw-colors#add-more-themes): ```typescript const { createThemes } = require('tw-colors'); module.exports = { content: ['./src/**/*.{html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], plugins: [ createThemes({ light: { 'primary': 'steelblue', 'secondary': 'darkblue', 'brand': '#F3F3F3', }, dark: { 'primary': 'turquoise', 'secondary': 'tomato', 'brand': '#4A4A4A', }, forest: { 'primary': '#2A9D8F', 'secondary': '#E9C46A', 'brand': '#264653', }, }) ], }; ``` It is a great improve on my point of view and you can certainly use it, but for me I would like to make my own code in my own way. ### CSS Variables TW Colors uses CSS variables to make the themes, so I thought I could use too as I already made a theme logic using them when working with Angular 2 years ago. Then I started thinking what are my needs and how can I solve them. The first thing was that I want as many themes as possible- So I would need to provide to every theme into different files as I wanted a lot of them, staying at the same file would not be very scalable. Second thing I wanted is to control the theme based on a html attribute `data-theme` as: ```html <html data-theme="dark"></html> ``` With this in mind, I thought on a simple solution: Use CSS variables, and change the value of them using Javascript. To start this I overwrite [Tailwind default colors](https://tailwindcss.com/docs/customizing-colors) with my colors in `Tailwind.config.ts` using CSS variables syntax as: ```typescript import type { Config } from 'tailwindcss' import defaultTheme from 'tailwindcss/defaultTheme' const config: Config = { content: [ './src/**/**/*.{js,ts,jsx,tsx,mdx}', ], theme: { colors: { 'primary': 'var(--primary)', 'secondary': 'var(--secondary)', 'text': 'var(--text)', 'title': 'var(--title)', 'foreground': 'var(--foreground)', 'background': 'var(--background)', 'background-card': 'var(--background-card)', 'text-on-primary': 'var(--text-on-primary)', 'text-hover': 'var(--text-hover)', 'primary-hover': 'var(--primary-hover)', 'code-header': 'var(--code-header)', 'transparent': 'transparent', 'current': 'currentColor', }, } } export default config ``` A little observation about two colors `transparent` and `current` that are been used as constants and not variables values, because I do not want to change them when the theme is changed. Continuing after colors configuration, I need to declare these variables and default values for them into some place so I went to `global.css` where [Tailwind is init](https://tailwindcss.com/docs/installation/using-postcss) and then declare the variables into a root [pseudo-class](https://developer.mozilla.org/en-US/docs/Web/CSS/:root): ```css @tailwind base; @tailwind components; @tailwind utilities; :root { --primary: #51C28A; --secondary: #74DFAA; --text: #DDDDDD; --title: #EAEAEA; --foreground: #282929; --background: #131313; --background-card: #ffffff0a; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #21222C; } ``` If you want, you can add a accessible [medias prefers color scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). That almost it, now you just need to add in some css file the values for each theme like so: ```css :root[data-theme='dark'] { --primary: #51C28A; --secondary: #74DFAA; --text: #DDDDDD; --title: #EAEAEA; --foreground: #282929; --background: #131313; --background-card: #ffffff0a; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #21222C; } ``` To make a better organization I like to make one file for each theme and then export all in one file. <Img src="https://github.com/fescherer/blog/assets/62115215/672b8aae-169a-4028-ace2-0f5482005252" name="Folder structure" alt="Folder structure" /> ```css /* themes/variants/dark.css */ :root[data-theme='dark'] { --primary: #51C28A; --secondary: #74DFAA; --text: #DDDDDD; --title: #EAEAEA; --foreground: #282929; --background: #131313; --background-card: #131313; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #21222C; } ``` ```css /* themes/variants/light.css */ :root[data-theme='light'] { --primary: #359967; --secondary: #4d9470; --text: #090a0a; --title: #191b1a; --foreground: #dfdfdf; --background: #ffffff; --background-card: #dfdfdf; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #21222C; } ``` ```css /* themes/index.css */ @import './variants/dark.css'; @import './variants/light.css'; ``` This way I can import the `index.css` file with all the imports already set up. The last part, we need to make the logic to change themes. To do this first you need to map all your themes into an array. ```typescript /* themes/themes.theme.ts */ export const themes = [ 'dark', 'light', ] ``` Lastly just change the value of the `html` attribute `data-theme` with Javascript and voilà. ```javascript document.getElementsByTagName('html')[0].setAttribute('data-theme', 'light') ``` ## SSR Themes preference If you analyze my blog you would probably noticed that theme preference is storage somewhere and then loaded before even the page. How that's possible? The key word is [`cookies`](https://web.dev/understanding-cookies). These type of data is storage and can be access by the server, not only the browser like [`local storage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage), using this, we can load the theme in server and then bring to browser the storage one as default. There are many ways to manage cookies and I think that every framework has its own way to do that. In this tutorial I will teach you with [NextJS 13 using app router](https://nextjs.org) because it is what I use and have experience. First we need to install 1 dependency called [cookies-next](https://www.npmjs.com/package/cookies-next) because with the [new API of app router, we can only use cookies at server components](https://nextjs.org/docs/app/api-reference/functions/cookies) but this solves just one part of the equation the load can be at server components, but every time we use interaction, the component need to be client, so how am I supposed to save a cookie of user theme choice? That's why we use `cookies-next`, to manipulate cookies at client components. ```bash npm install cookies-next ``` Let's start in the root `layout.tsx`. ```jsx export default function RootLayout({ children, }: { children: React.ReactNode }) { const cookieTheme = cookies().get('data-theme') const theme = themes.includes(cookieTheme?.value ?? '') ? cookieTheme?.value : 'dark' return ( <html lang="en" data-theme={theme}> <body> <main> {children} </main> </body> </html> ) } ``` Using the code above you can load the cookie `data-theme` and then pass to `html`. Now to change this just use the code Javascript that I said to you earlier. ```jsx // Here you can storage these values together with the themes like I showed you earlier. const themes = [ 'dark', 'light', ] function handleTheme(theme: string) { document.getElementsByTagName('html')[0].setAttribute('data-theme', theme) } export function ThemeSelector() { return ( <div> { themes.map((theme) => ( <button key={theme} onClick={() => handleTheme(theme)}> {theme} </button> )) } </div> ) } ``` ## User preferences - prefers-color-scheme, prefers-reduced-motion, etc One last thing you can do to make your code even better is to add a default theme based on user preference, it is very common to add a option on theme selector saying `default` or `system`, this is a theme based on user's system theme. So that's really import to make our interface looks like the user's on first time accessing our site. To do that is very simple, just add a [CSS query prefers-color-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). ```css /* themes/variants/system.css */ @media (prefers-color-scheme: dark) { :root { --primary: #51C28A; --secondary: #74DFAA; --text: #DDDDDD; --title: #EAEAEA; --foreground: #282929; --background: #131313; --background-card: #282929; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #21222C; } } @media (prefers-color-scheme: light) { :root { --primary: #359967; --secondary: #4d9470; --text: #090a0a; --title: #191b1a; --foreground: #dfdfdf; --background: #ffffff; --background-card: #dfdfdf; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #f3e9de; } } ``` ## Conclusion In this article I told you a little about the history behind Tailwind CSS creation, with the explain about what are themes and how people use them with Tailwind. For last how I would implement themes by myself using Tailwind and NextJS SSR feature to load theme in the server rendering time. Thank you for your time. Have a nice day. 😎✨✨✨😎