PostCSS in Laravel-mix

Yazeed Obaid
4 min readMay 25, 2019

--

postcss and laravel-mix

Built on top of Webpack, Laravel mix provide a fluent and easy to use api to manage application assets and to define the build process for the application. In the other way around, PostCSS is a great tool to operate on an application CSS styles using Javascript. Simply, PostCSS apply Javascript routines to application CSS. Those routines are called PostCSS plugins.

In this blog post I’ll explain how to use postCSS in Laravel-mix? How to use postcss-loader Webpack loader in Laravel-mix? And how to use the loader in laravel-mix with its config file to provide a clean way to define PostCSS plugins using the config file?

The first Attempt

Mix has support for common CSS pre-processors including postCSS. To bring postCSS to Laravel-mix we can use the postCss api on mix like so:

// webpack.mix.jsconst mix = require('laravel-mix');mix.js('resources/js/app.js', 'public/js')
.postCss('resources/css/app.css', 'public/css');

Easy right? Taking this a step further, postCss method also take a third argument as an array of postCSS plugins.

// webpack.mix.jsconst mix = require('laravel-mix');// A purgecss extractor class to define which unused CSS classes to // remove
TailwindExtractor = class TailwindExtractor {
static extract(content) {
return content.match(/[A-Za-z0-9-_:/]+/g) || []
}
};
mix.js('resources/js/app.js', 'public/js')
.postCss('resources/css/app.css', 'public/css', [
require('tailwindcss'),
require('@fullhuman/postcss-purgecss')({
content: [
'./resources/js/*/*.vue',
],
whitelist: ['html', 'body'],
extractors: [
{
extractor: TailwindExtractor,
extensions: ['vue']
}
]
}),
require('cssnano')
])

I have added tailwindCSS (Tailwind is a utility-first CSS framework), purgecss and cssnano plugins. Mix will pipe and execute the plugins in the same order as we specify them. the above plugins will do the following; PostCSS will call tailwindCSS to generate its CSS, purgecss will remove unused TailwindCSS classes using the extractor defined above. Finally cssnano will minify our CSS file .

Under the hood, mix at the end of the build will add the autoprefixer plugin to add cross-browser support for your CSS. Cool right?

What about using postcss-loader in Mix?

postcss-loader Webpack loader provide a clean way to use postCSS in an application be defining a config file for the postCSS plugins.

We can install the loader using npm;

$ npm i -D postcss-loader

To use the loader in our build, mix has rescued us again with another api. Mix provide an api to modify its default Webpack config file using webpackConfig method.

// webpack.mix.jsconst mix = require('laravel-mix');mix.js('resources/js/app.js', 'public/js')
.postCss('resources/css/app.css', 'public/css')
.webpackConfig({
module: {
rules: [
{
test: /\.css/,
use: [{loader: "postcss-loader", options: {}}]
}
]
}
})

Now postcss-loader with its config file is in your hands. You can now define your postCSS plugins in postcss.config.js file. Back to our example above, here the resulted postcss.config.js file which contains our postCSS plugins from mix config file:

// postcss.config.jsconst TailwindExtractor = class TailwindExtractor {
static extract(content) {
return content.match(/[A-Za-z0-9-_:/]+/g) || []
}
};

module.exports = {
plugins: {
'tailwindcss': {},
'@fullhuman/postcss-purgecss': {
content: [
'./resources/js/*/*.vue',
],
whitelist: ['html', 'body'],
extractors: [
{
extractor: TailwindExtractor,
extensions: ['vue']
}
]
},
'cssnano': {}
}
};

Everything works as we want! right? But we don’t want things to just work? Let us revise what we have done. We have modified mix default Webpack config file by adding postcss-loader to the build and eventually to Webpack config file. But, it feels that this is not the right place to put this type of configurations in, so let us clean things up!

Its time for some Clean ups!

Let us imagine that we want to use the postcss-loader with a method call, let us write that down;

// webpack.mix.jsrequire('postCssConfig');mix.js('resources/js/app.js', 'public/js')
.postCss('resources/css/app.css', 'public/css')
.postCssConfig();

Much much cleaner…

How can we define this method on mix object? Simply by extending mix! Mix provide a way to extend its functionality. As described in the documentation, we can define a class with predefined methods that we can work on. Our api class can be described as follows;

// postCssConfig.jsconst mix = require(“laravel-mix”);class LaravelMixPostCssConfigPlugin {

name() {
return "postCssConfig";
}

dependencies() {
return ["postcss-loader"];
}

register(options = {}) {
this.options = options;
}

webpackRules() {
return {
rules: [
{
test: /\.css/,
use: [{ loader: "postcss-loader", options: this.options }]
}
]
};
}
}

mix.extend("postCssConfig", new LaravelMixPostCssConfigPlugin());

I have used three methods from the class that mix define for its extension that are relevant to our api. The name method is used to define the method name that we want to call our api with. dependencies define any npm packages that we depend on. And webpackRules method define our api. The later will merge the rule(s) we provide with mix’s default rules. the last statement register our api to mix. And that’s it! Our webpack.mix.js file is much better and cleaner.

Conclusion

keeping mix config file simpler and cohesion for defining build processes, and extracting configurations and plugins to their own files, will make our build process maintainable and easy to interact with.

I have created an npm package that define this laravel-mix plugin we have built in this blog post. You can check it out on GitHub;

Resources:

  1. Laravel-mix documentation
  2. postcss-loader
  3. Webpack loaders and rules
  4. TailwindCSS

--

--

Yazeed Obaid

Software Engineer who loves to travel and discover new trends in house design.