Using Tailwind + CSS Modules + SASS and Stylelint with Next.js
April 6, 2021 Update
This article was originally based on Tailwind CSS v1.x. Today I've had some time to upgrade to v2.1 and it didn't require any configuration changes beyond what is noted in Tailwind CSS Upgrade Guide. If you are starting from scratch the most important changes are that post-css
and autoprefixer
are now required dependencies.
When I first built this site I chose SCSS along with CSS Modules to style it because I was familiar with this approach. That was pretty easy to do with help of the respective sections in Next.js documentation.
After having a working version of the site I revisited the idea to finally play a little with Tailwind CSS and see if it could fit with in my workflow and styles architecture. I set the following requirements that I wanted the integration had to satisfy:
- It should allow to define base styles for the whole site.
- It should be possible to keep styles separated in SCSS partial files.
- It shoulld allow to define styles using Tailwind in CSS Modules.
- Allow me to take advantage of Tailwind CSS and its flexibility.
- Allow nesting styles with SCSS
After a couple hours trying different approaches and configurations I finally was able to come up with a working solution that satisfies my needs.
Let's jump into the solution and implementation!
1. Configuration
1.1. Pre-requisites
I previously had SCSS and CSS Modules working on my site. Follow the instructions in the docs and you should be ready.
1.2. Add Tailwind CSS to the Project
For this I simply followed the Tailwind CSS example in the Next.js repository.
1.3. Configure Tailwind
Nothing special to do here. I just modified tailwind.config.js
to adjust the paths to my components and pages folders for the purge
directive as I have my source code under the ./src
. I also extended the colors
and fonts
configurations.
module.exports = {
purge: [
'./src/components/**/*.{js,ts,jsx,tsx}',
'./src/pages/**/*.{js,ts,jsx,tsx}'
],
theme: {
extend: {
colors: {
black: '#000000',
cornflower: {
default: '#8ec6eb',
medium: '#b1d7f1',
light: '#e5f2fa'
},
'steel-blue': {
default: '#4b89b3',
dark: '#162a39'
},
white: {
default: '#ffffff',
'transparent-10': 'rgba(255 255 255 / 10%)'
}
},
fontFamily: {
primary: ['"Noto Serif"', 'serif'],
secondary: ['"Quattrocento Sans"', 'sans-serif']
}
}
},
variants: {},
plugins: []
};
2. Using Tailwind in Base Styles
2.1. Creating the Main SCSS file for Styles.
I Added a src/styles/main.scss
containing the imports of SCSS partials files. As you can see in the following example it's not really different than what you do in the normal implementation:
@tailwind base;
/* Write your own custom base styles here */
@import './_base.scss';
@import './_typography.scss';
/* Start purging... */
@tailwind components;
/* Stop purging. */
/* Write you own custom component styles here */
/* Start purging... */
@tailwind utilities;
/* Stop purging. */
/* Your own custom utilities */
The only difference here is that we use imports instead of declaring styles right in the file. This is something I didn't like about Tailwind but this seemed a good workaround for me.
2.2. Declaring Base Styles Using Tailwind
Let's take a look on how to work with Tailwind in our SCSS files.
// file: ./src/styles/_typography.scss
// Importing external CSS code, fonts, etc.
@import url('https://fonts.googleapis.com/css2?family=Quattrocento+Sans:ital,wght@0,400;0,700;1,400&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Noto+Serif:ital,wght@0,400;0,700;1,400&display=swap');
// Using Tailwind with `@apply` in rules
h1,
h2,
h3 {
@apply my-4
font-secondary
text-cornflower
font-normal;
}
h1 {
@apply text-3xl;
// You can use normal CSS properties too for properties Tailwind doesn't handle:
background-color: rgba(0, 0, 0, 0.35);
@screen lg {
// Important: note that you can't use `lg:text-4xl` as you would
// normally do when working with classes in HTML. For this you
// must use the `@screen` directive and the respective breakpoint.
@apply text-4xl;
}
}
a {
@apply text-cornflower
underline;
&:hover {
// Important: similar to what we did for media queries, we must
// declare styles for pseudo-selectors using its own rule.
// Using `hover:text-white` will throw an error when styles compile.
@apply text-white;
}
}
2.3. Import Styles in ./pages/_app.js
I simply added the SCSS file import:
import Head from 'next/head';
import '../styles/main.scss';
// This default export is required in a new `pages/_app.js` file.
export default function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />;
}
That's it! You should have your base styles implementation working wonderfully at this point! Important: this requires to restart the Next.js service.
3. Using Tailwind in CSS Modules
To add styles using Tailwind in our CSS Modules for components is pretty much the same we did for our base styles.
3.1. Import the styles module file in our component
// file: `./src/components/Header/Header.jsx`
import styles from './Header.module.scss';
const Header = () => {
return <header className={styles.wrapper}>{/* component code... */}</header>;
};
export default Header;
3.2. Add module styles
I used @apply
, nesting and Media Queries the same way I did for base styles:
.wrapper {
@apply py-4
border-b
border-solid
border-white-transparent-10;
@screen lg {
@apply border-0
pt-10
pb-8;
}
}
Done! Now your component is styled with Tailwind CSS! 🙌
4. Bonus: Configure Stylelint to Format and Order Properties automatically
Note: this is intended for VSCode users but can be easily done in npm scripts or translated to other code editors.
4.1. Install Dependencies
yarn add --dev stylelint stylelint-config-recommended-scss stylelint-order stylelint-scss
4.2. Add Stylelint Configuration
/* file: ./.stylelintrc.json (remove this comment, no valid JSON) */
{
"extends": "stylelint-config-recommended-scss",
"plugins": ["stylelint-order", "stylelint-scss"],
"rules": {
"order/properties-alphabetical-order": true,
"scss/at-rule-no-unknown": null,
"scss/at-import-no-partial-leading-underscore": null
}
}
4.3. Install VSCode extension and configure it for your project folder
// file: ./.vscode/settings.json
{
"editor.formatOnSave": true,
// Run Stylelint fix when you save the file
"editor.codeActionsOnSave": {
"source.fixAll.stylelint": true
},
// Recommended config for the extension
"css.validate": false,
"less.validate": false,
"scss.validate": false
}
You're ready. Now when you save your .scss
file you should see Stylelint applying the fixes and sorting properties:
Well. It's not very hard to get it to work once you've put all the pieces together and connected correctly. Don't you think?
I hope these notes can be helpful to anyone who may be having a hard time implementing something similar. I would love to see your comments, corrections or questions as well as alternative approaches you could have.