• Stars
    star
    541
  • Rank 82,114 (Top 2 %)
  • Language
    JavaScript
  • License
    Other
  • Created over 9 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

PostCSS plugin to transform CSS Custom Properties(CSS variables) syntax into a static representation

PostCSS CSS Variables

npm version Build Status Gitter

PostCSS plugin to transform CSS Custom Properties (CSS variables) syntax into a static representation. This plugin provides a future-proof way of using most of CSS variables features, including selector cascading with some caveats, because this can only see the CSS, not the potentially dynamic HTML and DOM the CSS is applied to.

Install

npm install postcss postcss-css-variables --save-dev

Table of Contents

Code Playground

Try it in the playground and see what you think! Just add some CSS and see to see the final transformed/compiled CSS. You can try anything here in the playground, too.

Usage

For more general PostCSS usage, look here.

var postcss = require("postcss");
var cssvariables = require("postcss-css-variables");

var fs = require("fs");

var mycss = fs.readFileSync("input.css", "utf8");

// Process your CSS with postcss-css-variables
var output = postcss([cssvariables(/*options*/)]).process(mycss).css;

console.log(output);

Syntax

Defining Custom Properties with --*

A custom property is any property whose name starts with two dashes --. A property must be in a rule.

Note: :root is nothing more than the selector for the root DOM node. Any other selector like .class, #id, or even #foo ~ .bar > span.baz works.

:root {
  --foo-width: 100px;
  --foo-bg-color: rgba(255, 0, 0, 0.85);
}

.foo {
  --foo-width: 100px;
  --foo-bg-color: rgba(255, 0, 0, 0.85);
}

Custom properties can be declared multiple times, but like variable scope in other languages, only the most specific one takes precedence.

:root {
  --some-color: red;
}

.foo {
  /* red */
  color: var(--some-color);
}

.bar {
  --some-color: blue;
  /* blue */
  color: var(--some-color);
}

.bar:hover {
  --some-color: green;
  /* Automatically gets a `color: green;` declaration because we `--some-color` used within scope elsewhere */
}

W3C Draft: CSS Custom Properties for Cascading Variables, section 2

Using Variables/Custom Properties with var()

.foo {
  width: var(--foo-width);
  /* You can even provide a fallback */
  background: var(--foo-bg-color, #ff0000);
}

W3C Draft: CSS Custom Properties for Cascading Variables, section 3

Features

At-rules like @media, @support, etc.

It's perfectly okay to declare CSS variables inside media queries and the like. It'll work just as you would expect.

:root {
  --width: 100px;
}

@media (max-width: 1000px) {
  :root {
    --width: 200px;
  }
}

.box {
  width: var(--width);
}

Will be transformed to:

.box {
  width: 100px;
}

@media (max-width: 1000px) {
  .box {
    width: 200px;
  }
}

Pseudo-classes and Elements

Psuedo-classes are also dealt with correctly, because it's easy to statically determine.

.foo {
  --foo-color: red;
  color: var(--foo-color);
}

.foo:hover {
  --foo-color: green;
}

Will be transformed to:

.foo {
  color: red;
}

.foo:hover {
  color: green;
}

Nested Rules

This pairs very well with postcss-nested or postcss-nesting, adding support for nested rules. For either, you must put the plugin before postcss-css-variables in the plugin stack so that the & references are expanded first (postcss-css-variables doesn't understand them). For example, with postcss-nested, your PostCSS setup would look like this:

var postcss = require("postcss");
var cssvariables = require("postcss-css-variables");
var nested = require("postcss-nested");

var fs = require("fs");

var mycss = fs.readFileSync("input.css", "utf8");

var output = postcss([
  // Flatten/unnest rules
  nested,
  // Then process any CSS variables
  cssvariables(/*options*/)
]).process(mycss).css;

console.log(output);

For a simple example with nesting:

.box-foo {
  --some-width: 150px;
  width: var(--some-width);

  .box-bar {
    width: var(--some-width);
  }
}

With also postcss-nesting, this will be transformed to:

.box-foo {
  width: 150px;
}

.box-foo .box-bar {
  width: 150px;
}

For a more complex example with a media query:

:root {
  --some-width: 150px;
}

.box-foo {
  width: var(--some-width);

  .box-bar {
    width: var(--some-width);
  }
}

@media (max-width: 800px) {
  .box-foo {
    --some-width: 300px;
  }
}

Will be transformed to:

.box-foo {
  width: 150px;
}

.box-foo .box-bar {
  width: 150px;
}

@media (max-width: 800px) {
  .box-foo {
    width: 300px;
  }

  .box-foo .box-bar {
    width: 300px;
  }
}

Why?

This plugin was spawned out of a discussion on the cssnext repo and a personal need.

There is another similar plugin available, postcss-custom-properties, although it restricts itself much more than this plugin, preferring partial spec conformance. This plugin has the same capabilities but also adds imperfect feature support which stem from not being able to know what the DOM will look like when you compile your CSS. We instead look at the explicit structure of your CSS selectors.

Interoperability and differences from postcss-custom-properties

Putting postcss-css-variables in place of postcss-custom-properties should work out of the box.

In postcss-custom-properties, CSS variable declarations are specifically restricted to the :root selector.

In postcss-css-variables, this is not the case and they may be declared inside any rule with whatever selector. The variables are substituted based on statically known CSS selector inheritance.

Here's a quick overview of the differences:

  • CSS variables may be declared in any selector like .foo or .foo .bar:hover, and is not limited to just :root
  • CSS variables may be declared in @media, @support, and other at-rules.
  • CSS variables may be declared in :hover and other psuedo-classes, which get expanded properly.
  • Variables in nested rules can be deduced with the help of postcss-nested or postcss-nesting.

Continue to the next section to see where some of these might be unsafe to do. There are reasons behind the ethos of why the other plugin, postcss-custom-properties, is very limited in what it supports, due to differing opinions on what is okay to support.

Caveats

When you declare a CSS variable inside one selector, but consume it in another, this does make an unsafe assumption about it which can be non-conforming in certain edge cases. Here is an example where these limitations result in non-conforming behavior.

Note the nested markup below. We only know about the DOM's inheritance from your CSS selectors. If you want nest multiple times, you need to be explicit about it in your CSS which isn't necessary with browser that natively support CSS variables. See the innermost <div class="title">

<div class="component">
  Black

  <div class="title">
    Blue

    <div class="decoration">
      Green

      <div class="title">Blue with this plugin, but green per spec</div>
    </div>
  </div>
</div>
.component {
  --text-color: blue;
}

.component .title {
  color: var(--text-color);
}

.component .decoration {
  --text-color: green;
  color: var(--text-color);
}

postcss-custom-properties avoids this problem entirely by restricting itself to just the :root selector. This is because the developers there would prefer to not support a feature instead of something almost-spec-compliant like what postcss-css-variables does.

Options

preserve (default: false)

Allows you to preserve custom properties & var() usage in output.

Possible values:

  • false: Removes --var declarations and replaces var() with their resolved/computed values.
  • true: Keeps var() declarations in the output and has the computed value as a fallback declaration. Also keeps computed --var declarations.
  • 'computed': Keeps computed --var declarations in the output. Handy to make them available to your JavaScript.
  • (declaration) => boolean|'computed' : function/callback to programmatically return whether preserve the respective declaration

variables (default: {})

Define an object map of variables in JavaScript that will be declared at the :root scope.

Can be a simple key-value pair or an object with a value property and an optional isImportant bool property.

The object keys are automatically prefixed with -- (according to CSS custom property syntax) if you do not provide it.

preserveInjectedVariables (default: true)

Whether to preserve the custom property declarations inserted via the variables option from final output.

A typical use case is CSS Modules, where you would want to avoid repeating custom property definitions in every module passed through this plugin. Setting this option to false prevents JS-injected variables from appearing in output CSS.

var postcss = require("postcss");
var cssvariables = require("postcss-css-variables");

postcss([
  cssvariables({
    variables: {
      "--some-var": "100px",
      "--other-var": {
        value: "#00ff00"
      },
      "--important-var": {
        value: "#ff0000",
        isImportant: true
      }
    }
  })
]).process(css, opts);

preserveAtRulesOrder (default: false)

Keeps your at-rules like media queries in the order to defined them.

Ideally, this would be defaulted to true and it will be in the next major version. All of the tests expecations need to be updated and probably just drop support for preserveAtRulesOrder: false

Quick Reference/Notes

Testing

We have a suite of Mocha tests. If you see something that doesn't have coverage, make an issue or pull request.

Run once:

npm install

Run whenever you want to test:

npm run test

More Repositories

1

node-usb-detection

List USB devices in system and detect changes on them.
C++
371
star
2

postcss-increase-specificity

Why? Dealing with CSS you can't remove(mainly from a 3rd party). Increases specificity of selectors.
JavaScript
51
star
3

svg-curve-lib

Bezier Curve and Elliptical Arc implementations - SVG Path commands
C
46
star
4

gulp-css-spriter

Sprite Sheet Generation from CSS source files. The best and different approach to sprite sheets.
JavaScript
38
star
5

FP-V-GA-Text

A simple to use VHDL module to display text on VGA display.
VHDL
34
star
6

Radius

Complete Unity Reference Project - Multiplayer, UI
JavaScript
26
star
7

C4D-Py-Utils

Cinema 4D Python scripts designed for random purposes
Python
22
star
8

VHDL-Pong

Straightforward Pong Game written in VHDL. Scoring and Multiplayer
VHDL
20
star
9

vga-simulator

JavaScript
14
star
10

markdown-icons

Easily display icon fonts in markdown.
Python
13
star
11

emoji-unicode-version

Get the unicode version for a given emoji name
JavaScript
9
star
12

sdp-blob

Turn a WebRTC Offer/Answer into a compact blob/token
JavaScript
9
star
13

macchiato

Mocha/Chai inspired C++ test framework for desktop and Arduino (BDD)
C++
8
star
14

gulp-image-diff

Image diff'ing tool that compares pixel by pixel.
JavaScript
8
star
15

airflow-fluid-simulator

Create airflow heat or fluid animation/diagram by painting a vector-field
JavaScript
8
star
16

postcss-reverse-media

Reverse/Invert media query parameters
JavaScript
4
star
17

STM32F3-Mouse

A HID Mouse Project for the STM32F3 Discovery board.
4
star
18

zig-neural-networks

A from scratch neural network library in Zig
Zig
3
star
19

zig-ocr-neural-network

A from scratch neural network implementation in Zig, trained against the MNIST dataset to recognize handwritten digits.
Zig
3
star
20

Super-Bounce

Super Bouncing Game just like Halo 2 - GDSE Game Jam
JavaScript
3
star
21

postcss-raw

Protect nodes inside @raw at-rules from being touched by other plugins.
JavaScript
3
star
22

render-components-demo

Awesome Module Bundling/Packaging concept!
JavaScript
2
star
23

irccloud-layout-demo

Responsive CSS-only experiment to combine the IRCCloud website and native Android app
CSS
2
star
24

zig-ocr-mnist-k-nearest-neighbors

Basic OCR example written in Zig using K-nearest neighbor against the MNIST dataset
Zig
1
star
25

Traces

HTML5 Browser Game - GDSE Anniversary Game Jam 2014 Project
JavaScript
1
star
26

matrix-spam-scripts

Quick and dirty scripts to find and cleanup spam in Matrix rooms
JavaScript
1
star
27

emoji-add-unicode-version

DEPRECATED: See https://github.com/MadLittleMods/emoji-unicode-version
JavaScript
1
star
28

postcss-fallback

Provide fallback values for properties without having duplicate declarations with PostCSS
JavaScript
1
star
29

KmeansImage

Project to find the Dominant Color of any Image using K-means Clustering
PHP
1
star
30

jquery-carouselss

jQuery Carousel plugin for HTML content/images. Listens to CSS transitions/animations when switching frames. CSS class-based states.
JavaScript
1
star
31

crispin-mulberry-fe-nat

Crispin & Mulberry - Nerdery FE NAT
JavaScript
1
star
32

postcss-selector-scope-utility

WIP
JavaScript
1
star
33

linux-notes

1
star
34

fps-aim-analyzer

WIP
Zig
1
star
35

tensorflow-demo-in-browser

Tensorflow with custom model to detect my pet bunny/rabbit
JavaScript
1
star