From 6e8ca278c88023686acd57f67bc0a093ef4daf98 Mon Sep 17 00:00:00 2001 From: Isiah Meadows Date: Thu, 16 Jun 2016 06:10:23 -0400 Subject: [PATCH 1/6] Fix, clean up, and clarify README [ci skip] Based on [this comment](https://github.com/madlittlemods/postcss-css-variables/issues/4#issuecomment-225443586). I've also added + fixed several other things, and I cleaned up the organization and presentation to be a little more cohesive. --- README.md | 261 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 166 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index 79eec49..364df45 100644 --- a/README.md +++ b/README.md @@ -2,29 +2,42 @@ # PostCSS CSS Variables -[PostCSS](https://github.com/postcss/postcss) plugin to transform [`CSS Custom Properties(CSS variables)`](http://dev.w3.org/csswg/css-variables/) syntax into a static representation. This plugin provides a future-proof way of using **most** of CSS variables features. - -CSS variables or CSS Custom Properties limited subset polyfill/shim. - -We strive for the most complete transformation but we/no plugin can achieve true complete parity according to the [specification](http://dev.w3.org/csswg/css-variables/) because of the DOM cascade unknowns. +``` +npm install postcss-css-variables --save-dev +``` -### [Changelog](https://github.com/MadLittleMods/postcss-css-variables/blob/master/CHANGELOG.md) +[PostCSS](https://github.com/postcss/postcss) plugin to transform [`CSS Custom Properties (CSS variables)`](http://dev.w3.org/csswg/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 +### Table of Contents -`npm install postcss-css-variables --save-dev` + - [Changelog](https://github.com/MadLittleMods/postcss-css-variables/blob/master/CHANGELOG.md) + - [Code Playground](#code-playground) + - [Usage](#usage) + - [Syntax](#syntax) + - [Defining Custom Properties: the `--*` family of properties](#defining-custom-properties-the----family-of-properties) + - [Using Cascading Variables: the `var()` notation](#using-cascading-variables-the-var-notation) + - [Features](#features) + - [Nested Rules](#nested-rules) + - [Why?](#why) + - [Interoperability](#interoperability) + - [Differences from `postcss-custom-properties`](#differences-from-postcss-custom-properties) + - [Caveats](#caveats) + - [Options](#options) + - [Quick Reference/Notes](#quick-referencenotes) + - [Testing](#testing) -# [Code Playground](https://madlittlemods.github.io/postcss-css-variables/playground/) +# Code Playground -[Try it](https://madlittlemods.github.io/postcss-css-variables/playground/) before you install it! +[Try it now](https://madlittlemods.github.io/postcss-css-variables/playground/) and see what you think! -Add some PostCSS and it will show you the transformed/compiled CSS. +Add some CSS with variables, and it will show you the transformed/compiled CSS. You can try any of these examples on the code playground, too. # Usage -You can try any of these examples on the [code playground](https://madlittlemods.github.io/postcss-css-variables/playground/). +[*For more general PostCSS usage, look here.*](https://github.com/postcss/postcss#usage) ```js +// A quick demo of the API. var postcss = require('postcss'); var cssvariables = require('postcss-css-variables'); @@ -42,14 +55,66 @@ var output = postcss([ console.log(output); ``` -[*For more general PostCSS usage, see this section*](https://github.com/postcss/postcss#usage) +# Syntax + +### Defining Custom Properties: the `--*` family of properties + +A [custom property](http://dev.w3.org/csswg/css-variables/#defining-variables) is any property whose name starts with two dashes (U+002D HYPHEN-MINUS). A property must be in a `rule`. + +You may declare them in a special `:root` selector that represents the root DOM node. + +```css +:root { + --foo-width: 100px; + --foo-bg-color: rgba(255, 0, 0, 0.85); +} +``` + +You may also declare them in a normal selector. + +```css +.foo { + --foo-width: 100px; + --foo-bg-color: rgba(255, 0, 0, 0.85); +} +``` + +A custom property can be declared multiple times, but only the last one takes precedence. + +```css +:root { + --foo-width: 100px; + --foo-width: 200px; /* This is the declaration used. */ +} + +.foo { + --foo-width: 100px; +} + +.foo { + --foo-width: 200px; /* This is the declaration used. */ +} +``` + +*[W3C Draft: CSS Custom Properties for Cascading Variables, section 2](http://dev.w3.org/csswg/css-variables/#defining-variables)* +### Using Cascading Variables: the `var()` notation -## At-rule support `@media`, `@support`, etc +You can expand a CSS variable by using [`var(--variable-name)`](http://dev.w3.org/csswg/css-variables/#using-variables) -You can add rules that declare CSS variables nested inside at-rules. You can even nest the at-rules however deep you want. +```css +.foo { + width: var(--foo-width); + /* You can even provide a fallback */ + background: var(--foo-bg-color, #ff0000); +} +``` -The following CSS: +*[W3C Draft: CSS Custom Properties for Cascading Variables, section 3](http://dev.w3.org/csswg/css-variables/#using-variables)* + +# Features + +It's perfectly okay to declare CSS variables inside media queries and the like. It'll work just as you would expect. ```css :root { @@ -81,38 +146,59 @@ will be processed to: } ``` -## Pseudo class and elements +Psuedo-classes are also dealt with correctly, because it's easy to statically determine. ```css .foo { - --foo-color: #ff0000; + --foo-color: red; color: var(--foo-color); } .foo:hover { - --foo-color: #00ff00; + --foo-color: green; } ``` -will be processed to: +The above will be transformed to this: ```css .foo { - color: #ff0000; + color: red; } .foo:hover { - color: #00ff00; + color: green; } ``` +It also works very well with `calc()`. + +```css +:root { + --page-margin: 1em; +} + +/* Easy centering */ +.wrapper { + width: calc(100% - 2 * var(--page-margin)); + margin-left: var(--page-margin); + margin-right: var(--page-margin); +} +``` +The above would transform to this: -## Nested rules +```css +.wrapper { + width: calc(100% - 2 * 1em); + margin-left: 1em; + margin-right: 1em; +} +``` -When using this feature, `postcss-css-variables` will output invalid CSS by itself(but you did input invalid CSS anyway). This feature is best paired with [`postcss-nested`](https://github.com/postcss/postcss-nested) in order to properly expand the rules. +### Nested Rules -Run `postcss-nested` before `postcss-css-variables` so that `postcss-nested` can properly expand the `&` references before we start resolving variable values. +This pairs very well with [`postcss-nested`](https://github.com/postcss/postcss-nested) or [`postcss-nesting`](https://github.com/jonathantneal/postcss-nesting), adding support for nested rules. For either one, you must put it before `postcss-css-variables` so that the `&` references can be expanded first (`postcss-css-variables` doesn't understand them). For example, with `postcss-nested`, your JS can look like this: ```js @@ -137,9 +223,7 @@ var output = postcss([ console.log(output); ``` -### Simple example - -The following CSS: +For a simple example with nesting: ```css .box-foo { @@ -152,21 +236,19 @@ The following CSS: } ``` -will be processed to: +The above will, between this and `postcss-nesting`, will be transformed into this: ```css .box-foo { width: 150px; +} - .box-bar { - width: 150px; - } +.box-foo .box-bar { + width: 150px; } ``` -### Complex example - -The following CSS: +For a more complex example, with a media query and a `:root` selector: ```css :root { @@ -188,113 +270,104 @@ The following CSS: } ``` -will be processed to: +The above will be transformed into this: ```css .box-foo { width: 150px; +} - .box-bar { - width: 150px; - } - - @media (max-width: 800px) { - .box-bar { - width: 300px; - } - } +.box-foo .box-bar { + width: 150px; } @media (max-width: 800px) { .box-foo { width: 300px; } + + .box-foo .box-bar { + width: 300px; + } } ``` - - -## Interoperability - -`postcss-css-variables` plays really nice with [`postcss-nested`](https://github.com/postcss/postcss-nested) in order to get a larger subset of CSS variables features. *See [Nested rules, Usage section](#nested-rules)* - -If you are using [`postcss-custom-properties`](https://github.com/postcss/postcss-custom-properties) previously, we have a compatible feature set and more so you can switch over without having to refactor any of your code. You can just start writing the new awesome stuff. - - # Why? This plugin was spawned out of a [discussion on the `cssnext` repo](https://github.com/cssnext/cssnext/issues/99) and a personal need. -There is another similar plugin available, [`postcss-custom-properties`](https://github.com/postcss/postcss-custom-properties), which at the moment, covers less of the [CSS-variable/custom-property draft/spec](http://dev.w3.org/csswg/css-variables/) compared to this plugin. It also happened to not cover the feature I was after and after dealing with it, making an incomplete prototype piggybacking off of it; I decided to write my own starting from scratch. I do use some of the same/similar unit tests because that just makes sense to get proper code coverage. +There is another similar plugin available, [`postcss-custom-properties`](https://github.com/postcss/postcss-custom-properties), although it restricts itself much more than this, preferring spec conformance over imperfect feature support. -### Differences from `postcss-custom-properties` +### Interoperability -The main features that we`postcss-css-variables` add/provide are: +If you are/were already using `postcss-custom-properties`, this should work out of the box, without issue, and other than differences in the JavaScript API, it's a drop-in replacement. The only difference in CSS handling is that this attempts much broader support, and you can just start taking advantage immediately. Note the [caveats](#caveats), in that although this does correctly support what `postcss-custom-properties` does, there are certain edge cases it doesn't get perfectly. - - No limitation on what scope CSS variables can be declared or used (`:root` or wherever) - - Proper value substitution based on explicit DOM/structure traversal - - At-rule support `@media`, `@support`, etc - - Nested rules which can be fully deduced with [`postcss-nested`](https://github.com/postcss/postcss-nested). - - Pseudo class/element support `:hover`, etc +### Differences from `postcss-custom-properties` +In `postcss-custom-properties`, CSS variable declarations are specifically restricted to the `:root` selector. In `postcss-css-variables`, this is not the case, but they may be declared anywhere, and the values are substituted based on statically known CSS selector inheritance. + - CSS variables may be declared in any normal selector, including many of the more complex ones. + - CSS variables may be in `@media`, `@support`, and other at-rules. + - `:hover` and other psuedo-classes are properly supported. +Continue to the next section to see where some of these might be unsafe to do. There are reasons why `postcss-custom-properties` is very limited in what it supports, due to differing opinions on what is okay to support. -# Syntax +# Caveats -### [Defining Custom Properties: the --* family of properties](http://dev.w3.org/csswg/css-variables/#defining-variables) +When you declare a CSS variable inside one selector, but consume it in another, this does make unsafe assumptions which can be non-conforming in certain edge cases. Here is an example where these limitations end up with non-conforming behavior. -A custom property is any property whose name starts with two dashes (U+002D HYPHEN-MINUS). A property must be in a `rule`. + - CSS: -*Note: `:root` is nothing more than the selector for the root DOM node. You could use any other selector (class, id, etc)* -``` -:root { - --foo-width: 100px; - --foo-bg-color: rgba(255, 0, 0, 0.85); -} -``` + ```css + .component { + --text-color: blue; + } -A custom property can be declared multiple times. + .component .header { + color: var(--text-color); + } -*See: [W3C Draft: CSS Custom Properties for Cascading Variables, section 2](http://dev.w3.org/csswg/css-variables/#defining-variables) for more information.* + .component .text { + --text-color: green; + color: var(--text-color); + } + ``` + + - Jade (for brevity) with resulting color: + ```jade + .component: Black text + .component: .header: Blue text + .component: .header: .text: Green text + .component: .header: .text: .header: Green text per spec, blue per this polyfill + ``` -### [Using Cascading Variables: the var() notation](http://dev.w3.org/csswg/css-variables/#using-variables) +This spec deviation is intentional, as there's nothing this tool can do about that. `postcss-custom-properties` avoids this problem entirely by restricting itself to just the `:root` selector, and this is because the developers there would prefer to not support a feature instead of supporting it almost-correctly like what `postcss-css-variables` does. +A related example in the JavaScript world is [Babel's](https://babeljs.io) transformation of [arrow functions](https://babeljs.io/docs/plugins/transform-es2015-arrow-functions/) and methods, where Babel does not prevent arrow functions or object methods from being called with `new`, nor does it modify the `prototype` property on them, for performance reasons. -``` -.foo { - width: var(--foo-width); - /* You can even provide a fallback */ - background: var(--foo-bg-color, #ff0000); -} -``` - -*See: [W3C Draft: CSS Custom Properties for Cascading Variables, section 3](http://dev.w3.org/csswg/css-variables/#using-variables) for more information.* - - - - -# Options: +# Options ### `preserve` (default: `false`) Allows you to preserve custom properties & var() usage in output. -Allowed values: + +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 + - `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. ### `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 +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. -``` +```js var postcss = require('postcss'); var cssvariables = require('postcss-css-variables'); @@ -315,7 +388,6 @@ postcss([ .process(css, opts); ``` - # Quick Reference/Notes - This plugin was spawned out of a [discussion on the `cssnext` repo](https://github.com/cssnext/cssnext/issues/99) @@ -325,7 +397,6 @@ postcss([ - [Investigate support for media-query scoped properties *on `postcss/postcss-custom-properties`*](https://github.com/postcss/postcss-custom-properties/issues/9) - [remove `:root` limitation by injecting rules with new declarations that just contains modified properties. *on `postcss/postcss-custom-properties`*](https://github.com/postcss/postcss-custom-properties/issues/1) - # Testing We have a suite of [Mocha](http://mochajs.org/) tests. If you see something that doesn't have coverage, make an issue or pull request. From c0053674ae67d5518c413e5453cc12a1f17f0743 Mon Sep 17 00:00:00 2001 From: Isiah Meadows Date: Sat, 18 Jun 2016 01:58:03 -0400 Subject: [PATCH 2/6] Address comments [ci skip] --- README.md | 187 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 117 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index 364df45..8941244 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,27 @@ -[![npm version](https://badge.fury.io/js/postcss-css-variables.svg)](http://badge.fury.io/js/postcss-css-variables) [![Build Status](https://travis-ci.org/MadLittleMods/postcss-css-variables.svg)](https://travis-ci.org/MadLittleMods/postcss-css-variables) - # PostCSS CSS Variables +[![npm version](https://badge.fury.io/js/postcss-css-variables.svg)](http://badge.fury.io/js/postcss-css-variables) [![Build Status](https://travis-ci.org/MadLittleMods/postcss-css-variables.svg)](https://travis-ci.org/MadLittleMods/postcss-css-variables) [![Gitter](https://badges.gitter.im/MadLittleMods/postcss-css-variables.svg)](https://gitter.im/MadLittleMods/postcss-css-variables?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +[PostCSS](https://github.com/postcss/postcss) plugin to transform [`CSS Custom Properties (CSS variables)`](http://dev.w3.org/csswg/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-css-variables --save-dev ``` -[PostCSS](https://github.com/postcss/postcss) plugin to transform [`CSS Custom Properties (CSS variables)`](http://dev.w3.org/csswg/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. - ### Table of Contents - - [Changelog](https://github.com/MadLittleMods/postcss-css-variables/blob/master/CHANGELOG.md) - [Code Playground](#code-playground) - [Usage](#usage) - [Syntax](#syntax) - [Defining Custom Properties: the `--*` family of properties](#defining-custom-properties-the----family-of-properties) - [Using Cascading Variables: the `var()` notation](#using-cascading-variables-the-var-notation) - [Features](#features) + - [At-rules like `@media`, `@support`, etc.](#at-rules-like-media-support-etc) + - [Pseudo-classes and Elements](#pseudo-classes-and-elements) - [Nested Rules](#nested-rules) + - [`calc()`](#calc) - [Why?](#why) - [Interoperability](#interoperability) - [Differences from `postcss-custom-properties`](#differences-from-postcss-custom-properties) @@ -25,19 +29,19 @@ npm install postcss-css-variables --save-dev - [Options](#options) - [Quick Reference/Notes](#quick-referencenotes) - [Testing](#testing) + - [Changelog](https://github.com/MadLittleMods/postcss-css-variables/blob/master/CHANGELOG.md) + # Code Playground -[Try it now](https://madlittlemods.github.io/postcss-css-variables/playground/) and see what you think! +[Try it in the playground](https://madlittlemods.github.io/postcss-css-variables/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. -Add some CSS with variables, and it will show you the transformed/compiled CSS. You can try any of these examples on the code playground, too. # Usage [*For more general PostCSS usage, look here.*](https://github.com/postcss/postcss#usage) ```js -// A quick demo of the API. var postcss = require('postcss'); var cssvariables = require('postcss-css-variables'); @@ -55,11 +59,12 @@ var output = postcss([ console.log(output); ``` + # Syntax ### Defining Custom Properties: the `--*` family of properties -A [custom property](http://dev.w3.org/csswg/css-variables/#defining-variables) is any property whose name starts with two dashes (U+002D HYPHEN-MINUS). A property must be in a `rule`. +A [custom property](http://dev.w3.org/csswg/css-variables/#defining-variables) is any property whose name starts with two dashes (U+002D HYPHEN-MINUS). A property must be in a rule. You may declare them in a special `:root` selector that represents the root DOM node. @@ -100,7 +105,7 @@ A custom property can be declared multiple times, but only the last one takes pr ### Using Cascading Variables: the `var()` notation -You can expand a CSS variable by using [`var(--variable-name)`](http://dev.w3.org/csswg/css-variables/#using-variables) +You can use a CSS variable with [`var(--variable-name)`](http://dev.w3.org/csswg/css-variables/#using-variables). ```css .foo { @@ -112,8 +117,11 @@ You can expand a CSS variable by using [`var(--variable-name)`](http://dev.w3.or *[W3C Draft: CSS Custom Properties for Cascading Variables, section 3](http://dev.w3.org/csswg/css-variables/#using-variables)* + # 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. ```css @@ -132,7 +140,7 @@ It's perfectly okay to declare CSS variables inside media queries and the like. } ``` -will be processed to: +This will be transformed to: ```css .box { @@ -146,6 +154,8 @@ will be processed to: } ``` +### Pseudo-classes and Elements + Psuedo-classes are also dealt with correctly, because it's easy to statically determine. ```css @@ -159,7 +169,7 @@ Psuedo-classes are also dealt with correctly, because it's easy to statically de } ``` -The above will be transformed to this: +This will be transformed to: ```css .foo { @@ -171,34 +181,9 @@ The above will be transformed to this: } ``` -It also works very well with `calc()`. - -```css -:root { - --page-margin: 1em; -} - -/* Easy centering */ -.wrapper { - width: calc(100% - 2 * var(--page-margin)); - margin-left: var(--page-margin); - margin-right: var(--page-margin); -} -``` - -The above would transform to this: - -```css -.wrapper { - width: calc(100% - 2 * 1em); - margin-left: 1em; - margin-right: 1em; -} -``` - ### Nested Rules -This pairs very well with [`postcss-nested`](https://github.com/postcss/postcss-nested) or [`postcss-nesting`](https://github.com/jonathantneal/postcss-nesting), adding support for nested rules. For either one, you must put it before `postcss-css-variables` so that the `&` references can be expanded first (`postcss-css-variables` doesn't understand them). For example, with `postcss-nested`, your JS can look like this: +This pairs very well with [`postcss-nested`](https://github.com/postcss/postcss-nested) or [`postcss-nesting`](https://github.com/jonathantneal/postcss-nesting), adding support for nested rules. For either one, you must use that plugin before `postcss-css-variables` in your setup so the `&` references can be expanded first (`postcss-css-variables` doesn't understand them). For example, with `postcss-nested`, your PostCSS setup can look like this: ```js @@ -210,7 +195,6 @@ var fs = require('fs'); var mycss = fs.readFileSync('input.css', 'utf8'); -// Process your CSS with postcss-css-variables var output = postcss([ // Flatten/unnest rules nestedcss, @@ -236,7 +220,7 @@ For a simple example with nesting: } ``` -The above will, between this and `postcss-nesting`, will be transformed into this: +With also `postcss-nesting`, this will be transformed to: ```css .box-foo { @@ -270,7 +254,7 @@ For a more complex example, with a media query and a `:root` selector: } ``` -The above will be transformed into this: +This will be transformed to: ```css .box-foo { @@ -292,6 +276,55 @@ The above will be transformed into this: } ``` +### `calc()` + +It also pairs well with [`postcss-calc`](https://github.com/postcss/postcss-calc) to reduce `calc()` expressions arising from using variables. + +```js +var postcss = require('postcss'); +var cssvariables = require('postcss-css-variables'); +var calc = require('postcss-calc'); + +var fs = require('fs'); + +var mycss = fs.readFileSync('input.css', 'utf8'); + +var output = postcss([ + // Process any CSS variables + cssvariables(/*options*/) + // Then reduce `calc()` expressions + calc() + ]) + .process(mycss) + .css; + +console.log(output); +``` + +```css +:root { + --page-margin: 1em; +} + +/* Easy centering */ +.wrapper { + width: calc(100% - 2 * var(--page-margin)); + margin-left: var(--page-margin); + margin-right: var(--page-margin); +} +``` + +This will be transformed to: + +```css +.wrapper { + width: calc(100% - 2em); + margin-left: 1em; + margin-right: 1em; +} +``` + + # Why? This plugin was spawned out of a [discussion on the `cssnext` repo](https://github.com/cssnext/cssnext/issues/99) and a personal need. @@ -300,51 +333,63 @@ There is another similar plugin available, [`postcss-custom-properties`](https:/ ### Interoperability -If you are/were already using `postcss-custom-properties`, this should work out of the box, without issue, and other than differences in the JavaScript API, it's a drop-in replacement. The only difference in CSS handling is that this attempts much broader support, and you can just start taking advantage immediately. Note the [caveats](#caveats), in that although this does correctly support what `postcss-custom-properties` does, there are certain edge cases it doesn't get perfectly. +If you are/were already using [`postcss-custom-properties`](https://github.com/postcss/postcss-custom-properties), this should work out of the box, without issue, and other than differences in the JavaScript API, it's a drop-in replacement. The only difference in CSS handling is that this attempts much broader support, and you can just start taking advantage immediately. Note the [caveats](#caveats), in that although this does correctly support what `postcss-custom-properties` does, there are certain edge cases it doesn't get perfectly. ### Differences from `postcss-custom-properties` -In `postcss-custom-properties`, CSS variable declarations are specifically restricted to the `:root` selector. In `postcss-css-variables`, this is not the case, but they may be declared anywhere, and the values are substituted based on statically known CSS selector inheritance. +In [`postcss-custom-properties`](https://github.com/postcss/postcss-custom-properties), CSS variable declarations are specifically restricted to the `:root` selector. In `postcss-css-variables`, this is not the case, but they may be declared anywhere, and the values 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, and they are evaluated properly. + - Variables in nested rules can be deduced with the help of [`postcss-nested`](https://github.com/postcss/postcss-nested) or [`postcss-nesting`](https://github.com/jonathantneal/postcss-nesting). - - CSS variables may be declared in any normal selector, including many of the more complex ones. - - CSS variables may be in `@media`, `@support`, and other at-rules. - - `:hover` and other psuedo-classes are properly supported. +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 whether broader, yet potentially incorrect, support is okay. -Continue to the next section to see where some of these might be unsafe to do. There are reasons why `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 unsafe assumptions which can be non-conforming in certain edge cases. Here is an example where these limitations end up with non-conforming behavior. +When declaring a CSS variable inside one selector, but consume it in another, this does make an unsafe assumption which can be non-conforming in certain edge cases. Here is an example where these limitations unavoidably result in non-conforming behavior. - - CSS: +Using the following CSS: - ```css - .component { - --text-color: blue; - } +```css +.component { + --text-color: blue; +} - .component .header { - color: var(--text-color); - } +.component .header { + color: var(--text-color); +} - .component .text { - --text-color: green; - color: var(--text-color); - } - ``` +.component .text { + --text-color: green; + color: var(--text-color); +} +``` - - Jade (for brevity) with resulting color: +When nesting the markup like this, you may get incorrect behavior, because this only knows the CSS inheritance, not the HTML structure. (Note the innermost `

`.) - ```jade - .component: Black text - .component: .header: Blue text - .component: .header: .text: Green text - .component: .header: .text: .header: Green text per spec, blue per this polyfill - ``` +```html +
+ Black -This spec deviation is intentional, as there's nothing this tool can do about that. `postcss-custom-properties` avoids this problem entirely by restricting itself to just the `:root` selector, and this is because the developers there would prefer to not support a feature instead of supporting it almost-correctly like what `postcss-css-variables` does. +

+ Blue + +

+ Green + +

Blue with this plugin, but green per spec

+

+

+ +``` + +This spec deviation is intentional, as there's nothing this tool can do about that. [`postcss-custom-properties`](https://github.com/postcss/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 supporting it almost-correct like what this plugin does. -A related example in the JavaScript world is [Babel's](https://babeljs.io) transformation of [arrow functions](https://babeljs.io/docs/plugins/transform-es2015-arrow-functions/) and methods, where Babel does not prevent arrow functions or object methods from being called with `new`, nor does it modify the `prototype` property on them, for performance reasons. # Options @@ -388,6 +433,7 @@ postcss([ .process(css, opts); ``` + # Quick Reference/Notes - This plugin was spawned out of a [discussion on the `cssnext` repo](https://github.com/cssnext/cssnext/issues/99) @@ -397,6 +443,7 @@ postcss([ - [Investigate support for media-query scoped properties *on `postcss/postcss-custom-properties`*](https://github.com/postcss/postcss-custom-properties/issues/9) - [remove `:root` limitation by injecting rules with new declarations that just contains modified properties. *on `postcss/postcss-custom-properties`*](https://github.com/postcss/postcss-custom-properties/issues/1) + # Testing We have a suite of [Mocha](http://mochajs.org/) tests. If you see something that doesn't have coverage, make an issue or pull request. From 519b11118c4988cad50b62b55e5f2d06404f0087 Mon Sep 17 00:00:00 2001 From: Isiah Meadows Date: Sat, 18 Jun 2016 02:09:57 -0400 Subject: [PATCH 3/6] Address comments [ci skip] --- README.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 8941244..41ffa8d 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ npm install postcss-css-variables --save-dev - [Code Playground](#code-playground) - [Usage](#usage) - [Syntax](#syntax) - - [Defining Custom Properties: the `--*` family of properties](#defining-custom-properties-the----family-of-properties) - - [Using Cascading Variables: the `var()` notation](#using-cascading-variables-the-var-notation) + - [Defining Custom Properties with `--*`](#defining-custom-properties-with---) + - [Using Variables/Custom Properties with `var()`](#using-variables-custom-properties-with-var) - [Features](#features) - [At-rules like `@media`, `@support`, etc.](#at-rules-like-media-support-etc) - [Pseudo-classes and Elements](#pseudo-classes-and-elements) @@ -62,11 +62,11 @@ console.log(output); # Syntax -### Defining Custom Properties: the `--*` family of properties +### Defining Custom Properties with `--*` -A [custom property](http://dev.w3.org/csswg/css-variables/#defining-variables) is any property whose name starts with two dashes (U+002D HYPHEN-MINUS). A property must be in a rule. +A custom property is any property whose name starts with two dashes (U+002D HYPHEN-MINUS). A property must be in a rule. -You may declare them in a special `:root` selector that represents the root DOM node. +*Note: `:root` is nothing more than the selector for the root DOM node.* ```css :root { @@ -75,7 +75,7 @@ You may declare them in a special `:root` selector that represents the root DOM } ``` -You may also declare them in a normal selector. +You may also declare custom properties in a normal selector. Any other selector like `.class`, `#id`, or even `#foo ~ .bar > span.baz` works. ```css .foo { @@ -103,9 +103,7 @@ A custom property can be declared multiple times, but only the last one takes pr *[W3C Draft: CSS Custom Properties for Cascading Variables, section 2](http://dev.w3.org/csswg/css-variables/#defining-variables)* -### Using Cascading Variables: the `var()` notation - -You can use a CSS variable with [`var(--variable-name)`](http://dev.w3.org/csswg/css-variables/#using-variables). +### Using Variables/Custom Properties with `var()` ```css .foo { From a9789a5c4c00acceab280e35236316c6257cf82d Mon Sep 17 00:00:00 2001 From: Isiah Meadows Date: Sat, 18 Jun 2016 02:16:08 -0400 Subject: [PATCH 4/6] Address comments [ci skip] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 41ffa8d..70c3a18 100644 --- a/README.md +++ b/README.md @@ -230,7 +230,7 @@ With also `postcss-nesting`, this will be transformed to: } ``` -For a more complex example, with a media query and a `:root` selector: +For a more complex example with a media query: ```css :root { From 0c6912edbe2cc3bdbae4b33ab3924580a1af68a7 Mon Sep 17 00:00:00 2001 From: Isiah Meadows Date: Sat, 18 Jun 2016 02:25:53 -0400 Subject: [PATCH 5/6] Address comments [ci skip] --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 70c3a18..fbbb1a0 100644 --- a/README.md +++ b/README.md @@ -335,7 +335,8 @@ If you are/were already using [`postcss-custom-properties`](https://github.com/p ### Differences from `postcss-custom-properties` -In [`postcss-custom-properties`](https://github.com/postcss/postcss-custom-properties), CSS variable declarations are specifically restricted to the `:root` selector. In `postcss-css-variables`, this is not the case, but they may be declared anywhere, and the values are substituted based on statically known CSS selector inheritance. +In [`postcss-custom-properties`](https://github.com/postcss/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. + Here's a quick overview of the differences: @@ -386,7 +387,7 @@ When nesting the markup like this, you may get incorrect behavior, because this ``` -This spec deviation is intentional, as there's nothing this tool can do about that. [`postcss-custom-properties`](https://github.com/postcss/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 supporting it almost-correct like what this plugin does. +This spec deviation is intentional, as there's nothing this tool can do about that. [`postcss-custom-properties`](https://github.com/postcss/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 supporting it almost-correctly like what this plugin does. # Options From 230c683427d8db0e4ecc150ee3ac2f426d06c528 Mon Sep 17 00:00:00 2001 From: Isiah Meadows Date: Sun, 3 Jul 2016 09:53:47 -0400 Subject: [PATCH 6/6] Update README.md --- README.md | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index fbbb1a0..f8fb35e 100644 --- a/README.md +++ b/README.md @@ -64,40 +64,44 @@ console.log(output); ### Defining Custom Properties with `--*` -A custom property is any property whose name starts with two dashes (U+002D HYPHEN-MINUS). A property must be in a rule. +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.* +*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.* ```css :root { --foo-width: 100px; --foo-bg-color: rgba(255, 0, 0, 0.85); } -``` - -You may also declare custom properties in a normal selector. Any other selector like `.class`, `#id`, or even `#foo ~ .bar > span.baz` works. -```css .foo { --foo-width: 100px; --foo-bg-color: rgba(255, 0, 0, 0.85); } ``` -A custom property can be declared multiple times, but only the last one takes precedence. +Custom properties can be declared multiple times, but like variable scope in other languages, only the most specific one takes precedence. ```css -:root { - --foo-width: 100px; - --foo-width: 200px; /* This is the declaration used. */ +:root: { + --some-color: red; } .foo { - --foo-width: 100px; + /* red */ + color: var(--some-color); } -.foo { - --foo-width: 200px; /* This is the declaration used. */ + +.bar { + --some-color: blue; + /* blue */ + color: var(--some-color); +} + +.bar:hover { + --some-color: green; + /* Automically gets a `color: green;` declaration because we `--some-color` used within scope elsewhere */ } ``` @@ -138,7 +142,7 @@ It's perfectly okay to declare CSS variables inside media queries and the like. } ``` -This will be transformed to: +Will be transformed to: ```css .box { @@ -167,7 +171,7 @@ Psuedo-classes are also dealt with correctly, because it's easy to statically de } ``` -This will be transformed to: +Will be transformed to: ```css .foo { @@ -350,7 +354,7 @@ Continue to the next section to see where some of these might be unsafe to do. T # Caveats -When declaring a CSS variable inside one selector, but consume it in another, this does make an unsafe assumption which can be non-conforming in certain edge cases. Here is an example where these limitations unavoidably result in non-conforming behavior. +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. Using the following CSS: