Switching from SASS to PostCSS
Tags: sass, postcss, frontend, css
Published: 2019-03-31 18:28:25

---
title: Switching from SASS to PostCSS
published: true
description: Tips for moving from SASS to PostCSS
tags: sass, postcss, frontend, css
---

Ever since [PostCSS][postcss] came out, I've exclusively used it for [Autoprefixer][autoprefixer]. I didn't really dig into the power of `PostCSS` and how it could actually be a comparable solution to [SASS][sass].

This article will go into depth about my skepticisms, and how I was able to find solutions using `PostCSS`.

# The Push

What originally considered me to switch was my interest in [Tailwind CSS][tailwind-css]. I love the ideas of `utility libraries`, but found it difficult to sell to other developers. However, we've been looking for ways to optimize development time, so `Tailwind` felt like a good choice. It's built with `PostCSS` in mind, so I at least wanted to give it a try.

# Previous Approach

My previous approach was a mix of [SASS][sass], [BEM][bem], [ITCSS][itcss], &amp; [SMACSS][smacss]. They solved the following issues:

- `SASS` - `variables, loops, imports, nested selectors, etc`
- `BEM` - reduce `specificity` and allow for easier additions / modifications to code
- `ITCSS` - (same as `BEM`), but also allows for an `inverted triangle` hierarchy for `specificity` gets more and more specific.
- `SMACSS` - used mostly for `modifier classes`, like `.js-is-active, .has-posts, etc`.

This worked really well for me. I saw the power of `utility libraries`, but figured I would let them evolve and try them out later.

# The Switch

I won't bore you with the details, but I went through many iterations like:

- `PostCSS` only, no `plugins` besides `Tailwind`
- `PostCSS + SASS`
- `PostCSS` only, with `Tailwind` and other complementary `plugins`

The core issue with the `PostCSS + SASS` approach was the inevitable duplication. For example, these situations can't work together:

```scss
/**
  * cannot set $font-size-px-base in PostCSS
  * since SASS gets run first, so would have
  * to duplicate code
*/
@function rem($pixels, $context: $font-size-px-base) {
  @if (unitless($pixels)) {
    $pixels: $pixels * 1px;
  }
  @if (unitless($context)) {
    $context: $context * 1px;
  }
  @return $pixels / $context * 1rem;
}

/**
  * can't use Tailwind variables
  * in the SASS function `scale` since
  * SASS runs before PostCSS
*/
.selector {
  font-size: scale(font-size, theme('fontSize.lg'), theme('fontSize.xl'));
}

/**
  * Interpolation was also a nightmare
  * I couldn't figure out how to make
  * something like this work
  * even with PostCSS Simple Vars
*/
.selector {
  $path: theme('path.theme');

  background-image: url($(path)/test.png);
}
```

## Final Solution

The final solution involved `PostCSS` only, with `Tailwind` and other complementary `plugins`.

When I thought about it, I was really using `SASS` for:

- `imports`
- `mixins`
- `functions`
- `variables`
- `nested selectors` - <small>_rarely, but nice to have, especially for pseudo selectors_</small>
- `loops` - <small>_very rarely, but nice to have_</small>

`PostCSS` has solutions for all of these once I realized the power of their plugin system. Furthermore, I found out some plugins, like for `functions` and `mixins`, allow you to inject `Javascript` functions into `PostCSS`. I was sold.

### Imports

[PostCSS Imports][postcss-imports] gives the exact same functionality as `SASS Imports`, so it was an easy switch.

```css
@import 'tailwindcss/base';

@import 'tailwindcss/components';
@import 'components/btn';

@import 'tailwindcss/utilities';
```

### Functions

[PostCSS Functions][postcss-functions] allows you to write functions `IN JAVASCRIPT` and inject them into `PostCSS`. `IN JAVASCRIPT. I LOVE JAVASCRIPT`.

```javascript
// functions.js

let settings = require('./settings');

let rem = (pixels, context = settings.defaults.fontSizePxBase) => {
  pixels = parseFloat(pixels);

  let result = pixels / context;

  return `${result}rem`;
};

module.exports = {
  rem,
};
```

### Mixins

[PostCSS Mixins][postcss-mixins] is the same deal as `PostCSS Functions`.

```javascript
// mixins.js

let placeholder = (mixin, immediateSelector = true) => {
  let vendors = [
    '::-webkit-input-placeholder',
    ':-moz-placeholder',
    '::-moz-placeholder',
    ':-ms-input-placeholder',
  ];

  return vendors.reduce((prev, vendor) => {
    let selector = immediateSelector ? `&${vendor}` : vendor;

    prev[selector] = {
      '@mixin-content': {},
    };

    return prev;
  }, {});
};

module.exports = {
  placeholder,
};
```

### Other Notable Plugins

- [PostCSS Nested][postcss-nested] - offers `SASS nesting`
- [PostCSS Simple Vars][postcss-simple-vars] - offers simple `variables` to be used in `selectors, etc`
- [PostCSS PurgeCSS][postcss-purge-css] - removes unnecessary `CSS` based on file globs you pass in
- [CSSNano][cssnano] - minify `CSS`
- [Autoprefixer][autoprefixer] - remove unnecessary `prefixes` based on your browser support

## Example PostCSS Config File

```javascript
// postcss.config.js

let functions = require('./functions');
let mixins = require('./mixins');

module.exports = function(context) {
  /**
   * context comes from what you pass
   * into `gulp-postcss`, omitting detailing here
   * since `gulp` is an implementation detail
   */
  let { options } = context;

  let plugins = [
    require('postcss-import')({
      path: [options.paths.components, options.paths.styles, 'node_modules'],
    }),
    require('tailwindcss')('./tailwind.config.js'),
    require('postcss-functions')({
      functions,
    }),
    require('postcss-mixins')({
      mixins,
    }),
    require('postcss-nested'),
    require('postcss-simple-vars'),
    require('autoprefixer'),
  ];

  return {
    plugins,
  };
};
```

I hope this at least got you interested in `PostCSS` and what it's powerful `plugin` system can provide for your next project.

[sass]: https://sass-lang.com/
[postcss]: https://github.com/postcss/postcss
[autoprefixer]: https://github.com/postcss/autoprefixer
[tailwind-css]: https://tailwindcss.com/docs/what-is-tailwind/
[bem]: http://getbem.com/
[itcss]: https://speakerdeck.com/dafed/managing-css-projects-with-itcss
[smacss]: https://smacss.com/
[postcss-functions]: https://github.com/andyjansson/postcss-functions
[postcss-imports]: https://github.com/postcss/postcss-import
[postcss-mixins]: https://github.com/postcss/postcss-mixins
[postcss-nested]: https://github.com/postcss/postcss-nested
[postcss-simple-vars]: https://github.com/postcss/postcss-simple-vars
[postcss-purge-css]: https://github.com/FullHuman/postcss-purgecss
[cssnano]: https://github.com/cssnano/cssnano