# PostCSS Advanced Variables [<img src="https://postcss.github.io/postcss/logo.svg" alt="PostCSS Logo" width="90" height="90" align="right">][postcss]

[![NPM Version][npm-img]][npm-url]
[![Build Status][cli-img]][cli-url]
[![Test Status][test-img]][test-url]
[![Support Chat][git-img]][git-url]

[PostCSS Advanced Variables] lets you use Sass-like variables, conditionals,
and iterators in CSS.

```scss
$dir: assets/icons;

@each $icon in (foo, bar, baz) {
  .icon-$icon {
    background: url('$dir/$icon.png');
  }
}

@for $count from 1 to 5 by 2 {
  @if $count > 2 {
    .col-$count {
      width: #{$count}0%;
    }
  }
}

@import "path/to/some-file";

/* after */

.icon-foo {
  background: url('assets/icons/foo.png');
}

.icon-bar {
  background: url('assets/icons/bar.png');
}

.icon-baz {
  background: url('assets/icons/baz.png');
}

.col-3 {
  width: 30%;
}

.col-5 {
  width: 50%;
}

// the contents of "path/to/_some-file.scss"
```

## Usage

Add [PostCSS Advanced Variables] to your build tool:

```bash
npm install postcss-advanced-variables --save-dev
```

#### Node

Use [PostCSS Advanced Variables] to process your CSS:

```js
require('postcss-advanced-variables').process(YOUR_CSS);
```

#### PostCSS

Add [PostCSS] to your build tool:

```bash
npm install postcss --save-dev
```

Use [PostCSS Advanced Variables] as a plugin:

```js
postcss([
  require('postcss-advanced-variables')(/* options */)
]).process(YOUR_CSS);
```

#### Gulp

Add [Gulp PostCSS] to your build tool:

```bash
npm install gulp-postcss --save-dev
```

Use [PostCSS Advanced Variables] in your Gulpfile:

```js
var postcss = require('gulp-postcss');

gulp.task('css', function () {
  return gulp.src('./src/*.css').pipe(
    postcss([
      require('postcss-advanced-variables')(/* options */)
    ])
  ).pipe(
    gulp.dest('.')
  );
});
```

#### Grunt

Add [Grunt PostCSS] to your build tool:

```bash
npm install grunt-postcss --save-dev
```

Use [PostCSS Advanced Variables] in your Gruntfile:

```js
grunt.loadNpmTasks('grunt-postcss');

grunt.initConfig({
  postcss: {
    options: {
      use: [
        require('postcss-advanced-variables')(/* options */)
      ]
    },
    dist: {
      src: '*.css'
    }
  }
});
```

---

## Features

### $variables

Variables let you store information to be reused anywhere in a stylesheet.

Variables are set just like CSS properties, placing a `$` symbol before the
name of the variable (`$var-name`). They may also be set placing a `$` symbol
before two parentheses wrapping the name of the variable (`$(var-name)`), or by
wrapping the `$` symbol and variable name in curly braces preceeded by a hash
(`#{$var-name}`).

```scss
$font-size:     1.25em;
$font-stack:    "Helvetica Neue", sans-serif;
$primary-color: #333;

body {
  font: $font-size $(font-stack);
  color: #{$primary-color};
}
```

*Note: To use `#{$var-name}` without issues, you will need to include the
[PostCSS SCSS Syntax].

In that example, `$font-size`, `$font-stack`, and `$primary-color` are replaced
with their values.

```css
body {
  font: 1.25em "Helvetica Neue", sans-serif;
  color: #333;
}
```

### @if and @else Rules

Conditionals like `@if` and `@else` let you use rules in a stylesheet if they
evaluate true or false.

Conditionals are set by writing `@if` before the expression you want to
evaluate. If the expression is true, then its contents are included in the
stylesheet. If the expression is false, then its contents are not included, but
the contents of an `@else` that follows it are included.

```scss
$type: monster;

p {
  @if $type == ocean {
    color: blue;
  } @else {
    color: black;
  }
}
```

In that example, `$type === ocean` is false, so the `@if` contents are ignored
and the `@else` contents are used.

```css
p {
  color: black;
}
```

### @for and @each Rules

Iterators like `@for` and `@each` let you repeat content in a stylesheet.

A `@for` statement repeats by a numerical counter defined as a variable.

It can be written as `@for $counter from <start> through <end>` where
`$counter` is the name of the iterating variable, `<start>` is the number to
start with, and `<end>` is the number to finish with.

It can also be written as `@for $counter from <start> to <end>` where
`$counter` is still the name of the counter variable, `<start>` is still the
number to start with, but `<end>` is now the number to finish
*before, but not include*.

When `<start>` is greater than `<end>`, the counter will decrement instead of
increment.

Either form of `@for` can be written as
`@for $var from <start> to <end> by <increment>` or
`@for $var from <start> through <end> by <increment>`
where `<incremement>` is the amount the counter variable will advance.

```scss
@for $i from 1 through 5 by 2 {
  .width-#{$i} {
    width: #{$i}0em;
  }
}

@for $j from 1 to 5 by 2 {
  .height-#{$j} {
    height: #{$j}0em;
  }
}
```

In that example, `$i` is repeated from 1 through 5 by 2, which means it is
repeated 3 times (1, 3, and 5). Meanwhile, `$j` is repeated from 1 to 5 by 2,
which means it is repeated 2 times (1 and 3).

```css
.width-1 {
  width: 10em;
}

.width-3 {
  width: 30em;
}

.width-5 {
  width: 50em;
}

.height-1 {
  height: 10em;
}

.height-3 {
  height: 30em;
}
```

An `@each` statement statement repeats through a list of values.

It can be written as `@each $item in $list` where `$item` is the
name of the iterating variable and `$list` is the list of values being looped
over.

```scss
@each $animal in (puma, sea-slug, egret, salamander) {
  .#{$animal}-icon {
    background-image: url("images/icon-#{$animal}.svg");
  }
}
```

In that example, a list of 4 animals is looped over to create 4 unique
classnames.

```css
.puma-icon {
  background-image: url("images/icon-puma.svg");
}

.sea-slug-icon {
  background-image: url("images/icon-sea-slug.svg");
}

.egret-icon {
  background-image: url("images/icon-egret.svg");
}

.salamander-icon {
  background-image: url("images/icon-salamander.svg");
}
```

It can also be written as `@each $item $counter in $list` where `$item` is
still the name of the iterating variable and `$list` is still the list of values
being looped over, but now `$counter` is the numerical counter.

```scss
@each $animal $i in (puma, sea-slug, egret, salamander) {
  .#{$animal}-icon {
    background-image: url("images/icon-#{$i}.svg");
  }
}
```

```css
.puma-icon {
  background-image: url("images/icon-1.svg");
}

.sea-slug-icon {
  background-image: url("images/icon-2.svg");
}

.egret-icon {
  background-image: url("images/icon-3.svg");
}

.salamander-icon {
  background-image: url("images/icon-4.svg");
}
```

In that example, a list of 4 animals is looped over to create 4 unique
classnames.

### @mixin, @include, and @content rules

Mixins let you reuse rule in a stylesheet. A `@mixin` defines the content you
want to reuse, while an `@include` rule includes it anywhere in your stylesheet.

Mixins are set by writing `@mixin` before the name of the mixin you define.
This can be (optionally) followed by comma-separated variables you
want to use inside of it. Mixins are then used anywhere by writing `@include`
before the name of the mixin you are using. This is (again, optionally)
followed by some comma-separated arguments you want to pass into the mixin as
the (aforementioned) variables.

```scss
@mixin heading-text {
  color: #242424;
  font-size: 4em;
}

h1, h2, h3 {
  @include heading-text;
}

.some-heading-component > :first-child {
  @include heading-text;
}
```

In that example, `@include heading-text` is replaced with its contents.

```css
h1, h2, h3 {
  color: #242424;
  font-size: 4em;
}

.some-heading-component > :first-child {
  color: #242424;
  font-size: 4em;
}
```

Remember, mixins can be followed by comma-separated variables you
want to pass into the mixin as variables.

```scss
@mixin heading-text($color: #242424, $font-size: 4em) {
  color: $color;
  font-size: $font-size;
}

h1, h2, h3 {
  @include heading-text;
}

.some-heading-component > :first-child {
  @include heading-text(#111111, 6em);
}
```

In that example, `@include heading-text` is replaced with its contents, but
this time some of their contents are customized with variables.

```css
h1, h2, h3 {
  color: #242424;
  font-size: 4em;
}

.some-heading-component > :first-child {
  color: #111111;
  font-size: 6em;
}
```

---

## Options

### variables

The `variables` option defines global variables used when they cannot be
resolved automatically.

```js
require('postcss-advanced-variables')({
  variables: {
    'site-width': '960px'
  }
});
```

The `variables` option also accepts a function, which is given 2 arguments; the
name of the unresolved variable, and the PostCSS node that used it.

```js
require('postcss-advanced-variables')({
  variables(name, node) {
    if (name === 'site-width') {
      return '960px';
    }

    return undefined;
  }
});
```

```scss
.hero {
  max-width: $site-width;
}

/* after */

.hero {
  max-width: 960px;
}
```

### unresolved

The `unresolved` option defines how unresolved variables, mixins, and imports
should be handled. The available options are `throw`, `warn`, and `ignore`. The
default option is to `throw`.

```js
require('postcss-advanced-variables')({
  unresolved: 'ignore' // ignore unresolved variables
});
```

### disable

The `disable` option defines which features should be disabled in
[PostCSS Advanced Variables].

The `disable` option can be a string or an array, and the features that can be
disabled are `@content`, `@each`, `@else`, `@if`, `@include`, `@import`, `@for`,
and `@mixin`.

```js
require('postcss-advanced-variables')({
  disable: '@mixin, @include, @content' // ignore @mixin, @include, and @content at-rules
});
```

### Import Options

These options only apply to the `@import` at-rule.

#### importPaths

The `importPaths` option defines a path or multiple paths used to lookup
files when they cannot be found automatically.

The `importPaths` option can be a string or an array.

By default, imports are resolved using the [Sass Import Resolve Specification].

```js
require('postcss-advanced-variables')({
  importPaths: ['path/to/files', 'another/path/to/files']
});
```

#### importResolve

The `importResolve` option defines the file resolver used by imports. It is a
function given 3 arguments; the url id, the current working directory, and the
options processed by [PostCSS Advanced Variables].

The `importResolve` function should return a Promise with an object containing
the full path (`file`) and the contents of the file (`contents`).

```js
const resolve = require('custom-resolver');

require('postcss-advanced-variables')({
  // a resolver may work many ways, and this is just an example
  importResolve: (id, cwd, opts) => resolve({ id, cwd });
});
```

#### importFilter

The `importFilter` option determines whether an import will be inlined.

The value can be a function or an regular expression. When
providing a function, it is called with a single string argument `id`
and returns true when the import should be inlined. When providing a
regular expression, if the `id` matches the expression, the import will
be inlined.

By default, imports are ignored if they begin with a protocol or
protocol-relative slashes (`//`).

```js
require('postcss-advanced-variables')({
  importFilter: (id) => {
    return ['ignore', 'these', 'imports'].contains(id);
  }
});
```

#### importRoot

The `importRoot` option defines the root directory used by imports when the
current directory cannot be detected. Its default value is `process.cwd()`.

```js
require('postcss-advanced-variables')({
  importRoot: 'path/to/root'
});
```

#### importCache

The `importCache` option defines a cache made available to the options object
that may be used by the [file resolver](#importResolve).

```js
const sharedCache = {};

require('postcss-advanced-variables')({
  importCache: sharedCache
});
```

[cli-img]: https://img.shields.io/travis/jonathantneal/postcss-advanced-variables.svg
[cli-url]: https://travis-ci.org/jonathantneal/postcss-advanced-variables
[test-img]: https://github.com/csstools/postcss-advanced-variables/actions/workflows/test.yml/badge.svg
[test-url]: https://github.com/csstools/postcss-advanced-variables/actions/workflows/test.yml
[git-img]: https://img.shields.io/badge/chat-gitter-blue.svg
[git-url]: https://gitter.im/postcss/postcss
[npm-img]: https://img.shields.io/npm/v/postcss-advanced-variables.svg
[npm-url]: https://www.npmjs.com/package/postcss-advanced-variables

[Gulp PostCSS]: https://github.com/postcss/gulp-postcss
[Grunt PostCSS]: https://github.com/nDmitry/grunt-postcss
[PostCSS]: https://github.com/postcss/postcss
[PostCSS Advanced Variables]: https://github.com/jonathantneal/postcss-advanced-variables
[PostCSS SCSS Syntax]: https://github.com/postcss/postcss-scss
[Sass Import Resolve Specification]: https://jonathantneal.github.io/sass-import-resolve/
