In this post I'll describe how to setup PostCSS plugins to work with Tailwind and Gridsome.
We start by creating a new project with gridsome create gridsome-tailwind-postcss
.
We will start by setting up Tailwind, move on to add PurgeCSS and finally configure other interesting PostCSS plugins to work with everything.
I set up a starter repository with every step on Github.
The Gridsome documentation already does a good job at explaining how to add Tailwind. This part of the post is based on this documentation and provided here so you don't have to go back and forth between multiple sources. The steps to setup Tailwind are :
main.css
filetailwind.config.js
filegridsome.config.js
npm install tailwindcss
Create a file main.css
in the /src/css
directory with the following content:
@tailwind base;
@tailwind components;
@tailwind utilities;
In main.js
import the newly created css file:
import '~/css/main.css'
import DefaultLayout from '~/layouts/Default.vue'
export default function (Vue, { router, head, isClient }) {
// Set default layout as a global component
Vue.component('Layout', DefaultLayout)
}
Create the TailwindCSS configuration file in /src
. You can generate it with:
npx tailwind init
This generates a minimal tailwind.config.js
at the root of your project:
module.exports = {
theme: {
extend: {}
},
variants: {},
plugins: []
}
We need to inform Gridsome that we are using TailwindCSS. To do so we add an import to TailwindCSS and update the configuration css.loaderOptions.postcss
.
const tailwind = require("tailwindcss");
const postcssPlugins = [tailwind()];
module.exports = {
siteName: "Gridsome",
plugins: [],
css: {
loaderOptions: {
postcss: {
plugins: postcssPlugins
}
}
}
};
In the existing src/pages/Index.vue
we add a button styled with TailwindCSS to verify that the setup is working properly :
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Button
</button>
Then we start (or restart) the server with gridsome develop
.
PurgeCSS allows us to remove unused CSS. It is particularly powerful when using Tailwind. To setup PurgeCSS the steps are:
purgecss.config.js
filegridsome.config.js
npm install -D @fullhuman/postcss-purgecss
Create a file named purgecss.config.js
in the root of the project with the configuration below:
module.exports = {
content: [
'./src/**/*.vue',
'./src/**/*.js',
'./src/**/*.jsx',
'./src/**/*.html',
'./src/**/*.pug',
'./src/**/*.md',
],
whitelist: [
'body',
'html',
'img',
'a',
'g-image',
'g-image--lazy',
'g-image--loaded',
],
extractors: [
{
extractor: content => content.match(/[A-z0-9-:\\/]+/g),
extensions: ['vue', 'js', 'jsx', 'md', 'html', 'pug'],
},
],
}
Update gridsome.config.js
to import purgecss
and add it to the postcss plugins.
Start by adding purgecss:
const purgecss = require('@fullhuman/postcss-purgecss')
You only really need purgeCSS in production, to add the plugin conditionnaly you can use the value of process.env.NODE_ENV
and check if it is equal to 'production'
.
The conditionnal import looks like this:
if (process.env.NODE_ENV === 'production') postcssPlugins.push(purgecss(require('./purgecss.config.js')))
Your file should now look like this:
const tailwind = require('tailwindcss')
const purgecss = require('@fullhuman/postcss-purgecss')
const postcssPlugins = [
tailwind(),
]
if (process.env.NODE_ENV === 'production') postcssPlugins.push(purgecss(require('./purgecss.config.js')))
module.exports = {
siteName: 'Gridsome',
plugins: [],
css: {
loaderOptions: {
postcss: {
plugins: postcssPlugins,
},
},
},
}
Great! PurgeCSS is now completely set up !
One interesting PostCSS plugin is CSSNext which gives us the ability to use future CSS specification today. Think of it of the Babel of CSS.
npm install postcss-cssnext
We add the PostCSS plugin to the Gridsome config as usual:
const tailwind = require("tailwindcss");
const cssnext = require("postcss-cssnext")
const purgecss = require("@fullhuman/postcss-purgecss");
const postcssPlugins = [cssnext(), tailwind()];
if (process.env.NODE_ENV === "production")
postcssPlugins.push(purgecss(require("./purgecss.config.js")));
module.exports = {
siteName: "Gridsome",
plugins: [],
css: {
loaderOptions: {
postcss: {
plugins: postcssPlugins
}
}
}
};
The order in which we load the plugins is important. Tailwind is not capable of reading next generation CSS so we need to transform it for Tailwind first.
If you are using VSCodium (or VSCode), you can use the plugin PostCSS Language Support to get syntax highlighting in your CSS files.
Let's test some features of CSSNext in our basic project. In main.css
add some css between @tailwind components
and @tailwind utilities
:
@tailwind base;
@tailwind components;
:root {
--mainColor: red;
}
button {
& a {
color: var(--mainColor)
}
@nest &.custom {
background-color: pink
}
}
@tailwind utilities;
We want @tailwind utilities
last to avoid overriding tailwind utilities.
Here we use a variable --mainColor
set to red
, and some nesting.
Let's update our Index.vue
to make use of our new CSS:
<button class="custom font-bold py-2 px-4 rounded"><a>Button</a></button>
Now if we start our server we should see a pink button with a red text. We managed to use CSSNext with tailwind!
Now let's say we have created multiple themes and split them across different files to better structure our project, our css folder could look like this:
main.css
base.css
theme-pink.css
theme-blue.css
If we want to import the css from theme-pink.css
into our project we can simply use an @import
statement in our main.css
file:
@tailwind base;
@tailwind components;
@import "./theme-pink.css";
@tailwind utilities;
CSSNext has been deprecated by its original author. A fork has been created named postcss-preset-env. Let's see how we can migrate our code to this new plugin. First we uninstall CSSNext:
npm uninstall postcss-cssnext
We install the new plugin:
npm install postcss-preset-env
And we update the configuration in gridsome.config.js
. By default, nesting is not activated, we configure the plugin to activate nesting with the following code:
presetEnv({
/* use stage 3 features + css nesting rules */
stage: 3,
features: {
'nesting-rules': true
}
})
The first part of our config file now looks like this:
const presetEnv = require("postcss-preset-env");
const postcssPlugins = [
presetEnv({
/* use stage 3 features + css nesting rules */
stage: 3,
features: {
"nesting-rules": true
}
}),
tailwind()
];
We can try and run the server and everything should be working properly.
What if we want to use Tailwind utilities directly in our css ?
To demonstrate this use case let's modify our Index.vue
file to add some more HTML.
<pre>
<code>
if (x==1) {
print("Hello")
}
</code>
</pre>
<p class="subtitle">
This gray text is semibold and uppercase
</p>
In our file theme-pink.css
we would like to add some CSS so that the code block has a dark pink background and the subtitle class is gray and uppercase.
We can use the directive @apply
followed by Tailwind utilities to do just that:
pre {
@apply bg-pink-900 rounded-t;
& code {
@apply text-gray-300;
}
}
.subtitle {
@apply text-gray-600 font-semibold uppercase bg-pink-200 rounded-b;
}
If we now run our server we can see that our modifications were taken into account.