diff --git a/README.md b/README.md index ad1dc2c..ea6978a 100644 --- a/README.md +++ b/README.md @@ -12,70 +12,56 @@ npm install postcss-css-variables --save-dev ### Table of Contents - - [Code Playground](#code-playground) - - [Usage](#usage) - - [Syntax](#syntax) - - [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) - - [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) - - [Changelog](https://github.com/MadLittleMods/postcss-css-variables/blob/master/CHANGELOG.md) - +- [Code Playground](#code-playground) +- [Usage](#usage) +- [Syntax](#syntax) - [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) - [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) +- [Changelog](https://github.com/MadLittleMods/postcss-css-variables/blob/master/CHANGELOG.md) # [Code Playground](https://madlittlemods.github.io/postcss-css-variables/playground/) [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. - # Usage -[*For more general PostCSS usage, look here.*](https://github.com/postcss/postcss#usage) +[_For more general PostCSS usage, look here._](https://github.com/postcss/postcss#usage) ```js -var postcss = require('postcss'); -var cssvariables = require('postcss-css-variables'); +var postcss = require("postcss"); +var cssvariables = require("postcss-css-variables"); -var fs = require('fs'); +var fs = require("fs"); -var mycss = fs.readFileSync('input.css', 'utf8'); +var mycss = fs.readFileSync("input.css", "utf8"); // Process your CSS with postcss-css-variables -var output = postcss([ - cssvariables(/*options*/) - ]) - .process(mycss) - .css; +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.* +_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); + --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); + --foo-width: 100px; + --foo-bg-color: rgba(255, 0, 0, 0.85); } ``` @@ -83,41 +69,39 @@ Custom properties can be declared multiple times, but like variable scope in oth ```css :root { - --some-color: red; + --some-color: red; } .foo { - /* red */ - color: var(--some-color); + /* red */ + color: var(--some-color); } - .bar { - --some-color: blue; - /* blue */ - color: var(--some-color); + --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 */ + --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](http://dev.w3.org/csswg/css-variables/#defining-variables)* +_[W3C Draft: CSS Custom Properties for Cascading Variables, section 2](http://dev.w3.org/csswg/css-variables/#defining-variables)_ ### Using Variables/Custom Properties with `var()` ```css .foo { - width: var(--foo-width); - /* You can even provide a fallback */ - background: var(--foo-bg-color, #ff0000); + 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](http://dev.w3.org/csswg/css-variables/#using-variables)* - +_[W3C Draft: CSS Custom Properties for Cascading Variables, section 3](http://dev.w3.org/csswg/css-variables/#using-variables)_ # Features @@ -127,17 +111,17 @@ It's perfectly okay to declare CSS variables inside media queries and the like. ```css :root { - --width: 100px; + --width: 100px; } @media (max-width: 1000px) { - :root { - --width: 200px; - } + :root { + --width: 200px; + } } .box { - width: var(--width); + width: var(--width); } ``` @@ -145,13 +129,13 @@ Will be transformed to: ```css .box { - width: 100px; + width: 100px; } @media (max-width: 1000px) { - .box { - width: 200px; - } + .box { + width: 200px; + } } ``` @@ -161,12 +145,12 @@ Psuedo-classes are also dealt with correctly, because it's easy to statically de ```css .foo { - --foo-color: red; - color: var(--foo-color); + --foo-color: red; + color: var(--foo-color); } .foo:hover { - --foo-color: green; + --foo-color: green; } ``` @@ -174,11 +158,11 @@ Will be transformed to: ```css .foo { - color: red; + color: red; } .foo:hover { - color: green; + color: green; } ``` @@ -186,24 +170,21 @@ Will be transformed to: 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, 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: - ```js -var postcss = require('postcss'); -var cssvariables = require('postcss-css-variables'); -var nested = require('postcss-nested'); +var postcss = require("postcss"); +var cssvariables = require("postcss-css-variables"); +var nested = require("postcss-nested"); -var fs = require('fs'); +var fs = require("fs"); -var mycss = fs.readFileSync('input.css', 'utf8'); +var mycss = fs.readFileSync("input.css", "utf8"); var output = postcss([ - // Flatten/unnest rules - nested, - // Then process any CSS variables - cssvariables(/*options*/) - ]) - .process(mycss) - .css; + // Flatten/unnest rules + nested, + // Then process any CSS variables + cssvariables(/*options*/) +]).process(mycss).css; console.log(output); ``` @@ -212,12 +193,12 @@ For a simple example with nesting: ```css .box-foo { - --some-width: 150px; - width: var(--some-width); + --some-width: 150px; + width: var(--some-width); - .box-bar { - width: var(--some-width); - } + .box-bar { + width: var(--some-width); + } } ``` @@ -225,11 +206,11 @@ With also `postcss-nesting`, this will be transformed to: ```css .box-foo { - width: 150px; + width: 150px; } .box-foo .box-bar { - width: 150px; + width: 150px; } ``` @@ -237,21 +218,21 @@ For a more complex example with a media query: ```css :root { - --some-width: 150px; + --some-width: 150px; } .box-foo { - width: var(--some-width); + width: var(--some-width); - .box-bar { - width: var(--some-width); - } + .box-bar { + width: var(--some-width); + } } @media (max-width: 800px) { - .box-foo { - --some-width: 300px; - } + .box-foo { + --some-width: 300px; + } } ``` @@ -259,26 +240,24 @@ Will be transformed to: ```css .box-foo { - width: 150px; + width: 150px; } .box-foo .box-bar { - width: 150px; + width: 150px; } @media (max-width: 800px) { - .box-foo { - width: 300px; - } + .box-foo { + width: 300px; + } - .box-foo .box-bar { - width: 300px; - } + .box-foo .box-bar { + width: 300px; + } } ``` - - # Why? This plugin was spawned out of a [discussion on the `cssnext` repo](https://github.com/cssnext/cssnext/issues/99) and a personal need. @@ -295,14 +274,13 @@ In `postcss-css-variables`, this is not the case and they may be declared inside 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`](https://github.com/postcss/postcss-nested) or [`postcss-nesting`](https://github.com/jonathantneal/postcss-nesting). +- 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`](https://github.com/postcss/postcss-nested) or [`postcss-nesting`](https://github.com/jonathantneal/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`](https://github.com/postcss/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. @@ -311,17 +289,17 @@ Note the nested markup below. We only know about the DOM's inheritance from your ```html
- Black + Black -
- Blue +
+ Blue -
- Green +
+ Green -
Blue with this plugin, but green per spec
-
+
Blue with this plugin, but green per spec
+
``` @@ -340,10 +318,8 @@ Note the nested markup below. We only know about the DOM's inheritance from your } ``` - [`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 something almost-spec-compliant like what `postcss-css-variables` does. - # Options ### `preserve` (default: `false`) @@ -352,10 +328,10 @@ 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'` : Handles preservation behavior based on the respective declaration. +- `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: `{}`) @@ -374,43 +350,36 @@ repeating custom property definitions in every module passed through this plugin prevents JS-injected variables from appearing in output CSS. ```js -var postcss = require('postcss'); -var cssvariables = require('postcss-css-variables'); +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); + 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 - - This plugin was spawned out of a [discussion on the `cssnext` repo](https://github.com/cssnext/cssnext/issues/99). - - We provide a larger CSS variable feature subset than [`postcss-custom-properties`](https://github.com/postcss/postcss-custom-properties). - - Related links and issues: - - [var declared in media query should pull in properties that use/reference that var *on `cssnext/cssnext`*](https://github.com/cssnext/cssnext/issues/99) - - [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) - +- This plugin was spawned out of a [discussion on the `cssnext` repo](https://github.com/cssnext/cssnext/issues/99). +- We provide a larger CSS variable feature subset than [`postcss-custom-properties`](https://github.com/postcss/postcss-custom-properties). +- Related links and issues: - [var declared in media query should pull in properties that use/reference that var _on `cssnext/cssnext`_](https://github.com/cssnext/cssnext/issues/99) - [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 diff --git a/test/fixtures/preserve-variables-conditionally.css b/test/fixtures/preserve-variables-conditionally.css index 2e2f203..33cb840 100644 --- a/test/fixtures/preserve-variables-conditionally.css +++ b/test/fixtures/preserve-variables-conditionally.css @@ -1,21 +1,21 @@ :root { - --color-one: #0000ff; - --color-two: #00ff00; - --color-three: var(--color-two); + --no-preserve: #0000ff; + --color-foo: #00ff00; + --color-bar: var(--color-two); } .before { - prop: before; - color: var(--color-one); + prop: before; + color: var(--no-preserve); } .after { - color: var(--color-two); - prop: after; + color: var(--color-foo); + prop: after; } .before-and-after { - prop: before; - color: var(--missing, #ff0000); - otherprop: after; -} \ No newline at end of file + prop: before; + color: var(--missing, #ff0000); + otherprop: after; +} diff --git a/test/fixtures/preserve-variables-conditionally.expected.css b/test/fixtures/preserve-variables-conditionally.expected.css index c9108ff..78a9d09 100644 --- a/test/fixtures/preserve-variables-conditionally.expected.css +++ b/test/fixtures/preserve-variables-conditionally.expected.css @@ -1,22 +1,22 @@ :root { - --color-two: #00ff00; - --color-three: var(--color-two); + --color-foo: #00ff00; + --color-bar: var(--color-two); } .before { - prop: before; - color: #0000ff; + prop: before; + color: #0000ff; } .after { - color: #00ff00; - color: var(--color-two); - prop: after; + color: #00ff00; + color: var(--color-foo); + prop: after; } .before-and-after { - prop: before; - color: #ff0000; - color: var(--missing, #ff0000); - otherprop: after; -} \ No newline at end of file + prop: before; + color: #ff0000; + color: var(--missing, #ff0000); + otherprop: after; +} diff --git a/test/test.js b/test/test.js index caafea2..a3704fa 100644 --- a/test/test.js +++ b/test/test.js @@ -1,302 +1,375 @@ -var Promise = require('bluebird'); -var fs = Promise.promisifyAll(require('fs')); -var path = require('path'); +var Promise = require("bluebird"); +var fs = Promise.promisifyAll(require("fs")); +var path = require("path"); -var chai = require('chai'); +var chai = require("chai"); var expect = chai.expect; -var chaiAsPromised = require('chai-as-promised'); +var chaiAsPromised = require("chai-as-promised"); chai.use(chaiAsPromised); -var postcss = require('postcss'); -var cssvariables = require('../'); -var cssnano = require('cssnano'); -var normalizeWhitespace = require('postcss-normalize-whitespace'); -var discardComments = require('postcss-discard-comments'); - -var MOCK_JS_VARIABLES = { - '--js-defined1': '75px', - '--js-defined2': { - value: '80px' - }, - '--js-defined-important': { - value: '#0f0', - isImportant: true - }, - // Should be automatically prefixed with `--` - 'js-defined-no-prefix': '#ff0000' +var postcss = require("postcss"); +var cssvariables = require("../"); +var cssnano = require("cssnano"); +var normalizeWhitespace = require("postcss-normalize-whitespace"); +var discardComments = require("postcss-discard-comments"); + +var MOCK_JS_VARIABLES = { + "--js-defined1": "75px", + "--js-defined2": { + value: "80px" + }, + "--js-defined-important": { + value: "#0f0", + isImportant: true + }, + // Should be automatically prefixed with `--` + "js-defined-no-prefix": "#ff0000" }; var NON_STRING_VARIABLES = { - 'number-value': 50, - 'zero-value': 0, - 'null-value': null, - 'undefined-value': undefined, - 'object-value-passed-by-mistake': {}, - 'true-value': true, - 'false-value': false, + "number-value": 50, + "zero-value": 0, + "null-value": null, + "undefined-value": undefined, + "object-value-passed-by-mistake": {}, + "true-value": true, + "false-value": false }; var testPlugin = function(filePath, expectedFilePath, options) { - options = options || {}; - return Promise.props({ - actualBuffer: fs.readFileAsync(filePath), - expectedBuffer: fs.readFileAsync(expectedFilePath) - }) - .then(function({ actualBuffer, expectedBuffer }) { - var actualResult = postcss([ - cssvariables(options), - cssnano({ - preset: { plugins: [normalizeWhitespace, discardComments] } - }) - ]) - .process(String(actualBuffer)); - - var expectedResult = postcss([ - cssnano({ - preset: { plugins: [normalizeWhitespace, discardComments] } - }) - ]) - .process(String(expectedBuffer)); - - return Promise.props({ - actualResult: actualResult, - expectedResult: expectedResult - }); - }) - .then(({ actualResult, expectedResult }) => { - expect(actualResult.css.replace(/\r?\n/g, '')).to.equal(expectedResult.css.replace(/\r?\n/g, '')); - }); + options = options || {}; + return Promise.props({ + actualBuffer: fs.readFileAsync(filePath), + expectedBuffer: fs.readFileAsync(expectedFilePath) + }) + .then(function({ actualBuffer, expectedBuffer }) { + var actualResult = postcss([ + cssvariables(options), + cssnano({ + preset: { plugins: [normalizeWhitespace, discardComments] } + }) + ]).process(String(actualBuffer)); + + var expectedResult = postcss([ + cssnano({ + preset: { plugins: [normalizeWhitespace, discardComments] } + }) + ]).process(String(expectedBuffer)); + + return Promise.props({ + actualResult: actualResult, + expectedResult: expectedResult + }); + }) + .then(({ actualResult, expectedResult }) => { + expect(actualResult.css.replace(/\r?\n/g, "")).to.equal( + expectedResult.css.replace(/\r?\n/g, "") + ); + }); }; -var fixtureBasePath = './test/fixtures/'; +var fixtureBasePath = "./test/fixtures/"; var test = function(message, fixtureName, options) { - it(message, function() { - return testPlugin( - path.join(fixtureBasePath, fixtureName + '.css'), - path.join(fixtureBasePath, fixtureName + '.expected.css'), - options - ); - }); + it(message, function() { + return testPlugin( + path.join(fixtureBasePath, fixtureName + ".css"), + path.join(fixtureBasePath, fixtureName + ".expected.css"), + options + ); + }); }; - - -describe('postcss-css-variables', function() { - // Just make sure it doesn't mangle anything - test('should work when there are no var() functions to consume declarations', 'no-var-func'); - test('should work when there are no var() functions(just `:root`) to consume declarations', 'no-var-func-just-root'); - test('should work when no variable name passed to `var()`', 'empty-var-func'); - - - test('should work with variables declared in root', 'root-variable'); - test('should work with variables with parenthesis in fallback', 'fallback-with-parenthesis'); - - test('should work with locally scoped variable in a non-root rule', 'local-variable-non-root'); - - - test( - 'should work with any combinator selector if the last piece is the variable we have in the map', - 'scope-last-piece-of-combinator-sequence' - ); - - - test('should work with descendant selector type "nesting"', 'descendant-selector'); - test('should work with css4 descendant selector type "nesting"', 'css4-descendant-selector'); - test('should work with direct descendant selector', 'direct-descendant-selector'); - - test( - 'should work with direct descendant selector where variables are scoped in a descendant selector', - 'direct-descendant-selector-descendant-scope' - ); - test( - 'should work with direct descendant selector where variables are scoped in a direct descendant selector', - 'direct-descendant-selector-direct-descendant-scope' - ); - - - test('should work with pseudo selectors', 'pseudo-selector'); - //test('should work with multiple pseudo selectors', 'pseudo-multi'); - test('should work with variables declared in pseudo selectors', 'pseudo-selector-declare-variable'); - - - - test('should work with variables defined in comma separated selector', 'comma-separated-variable-declaration'); - - - test('should work use the correct variable in comma separated selector', 'comma-separated-variable-usage'); - - - test('should work with star selector', 'star-selector-scope'); - - test('should work with `!important` variable declarations', 'important-variable-declaration'); - - - - describe('with at-rules', function() { - test('should add rule declaration of property in @media', 'media-query'); - test('should add rule declaration of property in @support', 'support-directive'); - - test('should work with @media, preserving rule order', 'media-query-preserve-rule-order', { preserveAtRulesOrder: true }); - - test('should work with nested @media', 'media-query-nested', { preserveAtRulesOrder: false }); - test('should work with nested @media, preserving rule order', 'media-query-nested-preserver-rule-order', { preserveAtRulesOrder: true }); - - - test('should cascade to nested rules', 'cascade-on-nested-rules'); - - test('should cascade with calc-expression to nested rules', 'cascade-with-calc-expression-on-nested-rules'); - - test('should cascade to nested rules in the proper scope. See issue #2', 'cascade-on-nested-rules-in-proper-scope'); - }); - - - test('should work with variables that reference other variables', 'variable-reference-other-variable'); - - test( - 'should work with variable with calc-expression that reference other variables', - 'variable-with-calc-expression-reference-other-variable' - ); - - test( - 'should work with variables that reference other variables with at-rule changing the value', - 'variable-reference-other-variable-media-query1' - ); - test( - 'should work with local variables that reference other variables with at-rule changing the value', - 'variable-reference-other-variable-media-query2' - ); - - - - test('should work with variables that try to self reference', 'self-reference'); - test('should work with variables that try to self reference and fallback properly', 'self-reference-fallback'); - test('should work with circular reference', 'circular-reference'); - - - describe('with `options.variables`', function() { - test( - 'should work with JS defined variables', - 'js-defined', - { variables: MOCK_JS_VARIABLES } - ); - test( - 'should work with JS defined important variables', - 'js-defined-important', - { variables: MOCK_JS_VARIABLES } - ); - test( - 'should preserve -- declarations and var() values with `options.variables` AND `options.preserve`', - 'js-defined-preserve', - { - variables: MOCK_JS_VARIABLES, - preserve: true - } - ); - test( - 'should preserve var() values and clean injected declarations with `options.variables` AND `options.preserve` AND `options.preserveInjectedVariables: false`', - 'js-defined-preserve-injected', - { - variables: MOCK_JS_VARIABLES, - preserve: true, - preserveInjectedVariables: false, - } - ); - test( - 'should cast non-string values to string', - 'js-defined-non-string-values-casted-to-string', - { - variables: NON_STRING_VARIABLES - } - ); - }); - - describe('with `options.preserve`', function() { - test( - 'preserves variables when `preserve` is `true`', - 'preserve-variables', - { preserve: true } - ); - - test( - 'preserves variables in @media when `preserve` is `true`', - 'preserve-variables-in-media', - { preserve: true } - ); - - test( - 'preserves computed value when `preserve` is `\'computed\'`', - 'preserve-computed', - { preserve: 'computed' } - ); - - test( - 'preserves variables when `preserve` function applies', - 'preserve-variables-conditionally', - { - preserve: function (declaration) { - return !( - declaration.value.includes("--color-one") - || declaration.prop.includes("--color-one") - ) - } - } - ); - }); - - - describe('missing variable declarations', function() { - test('should work with missing variables', 'missing-variable-usage'); - test('should use fallback value if provided with missing variables', 'missing-variable-should-fallback'); - it('should use string values for `undefined` values, see #22', function() { - return fs.readFileAsync('./test/fixtures/missing-variable-usage.css', 'utf8') - .then(function(buffer) { - var contents = String(buffer); - return postcss([ - cssvariables() - ]) - .process(contents) - .then(function(result) { - var root = result.root; - var fooRule = root.nodes[0]; - expect(fooRule.selector).to.equal('.box-foo'); - var colorDecl = fooRule.nodes[0]; - expect(colorDecl.value).to.be.a('string'); - expect(colorDecl.value).to.be.equal('undefined'); - - expect(result.warnings().length).to.be.equal(1); - expect(result.warnings()[0].type).to.be.equal('warning'); - expect(result.warnings()[0].text).to.be.equal('variable --missing is undefined and used without a fallback'); - }); - }); - }); - test('should use fallback variable if provided with missing variables', 'missing-variable-should-fallback-var'); - test('should use fallback variable if provided with missing variables calc', 'missing-variable-should-fallback-calc'); - test('should use fallback variable if provided with missing variables nested', 'missing-variable-should-fallback-nested'); - test('should not mangle outer function parentheses', 'nested-inside-other-func'); - test('should not mangle outer function parentheses - with fallback', 'nested-inside-other-func-with-fallback'); - test('should not mangle outer function parentheses - calc', 'nested-inside-calc-func'); - test('should not mangle outer function parentheses - calc with fallback', 'nested-inside-calc-func-with-fallback'); - test('should not mangle outer function parentheses - calc with fallback var()', 'nested-inside-calc-func-with-fallback-var'); - }); - - test('should accept whitespace in var() declarations', 'whitespace-in-var-declaration' ) - - it('should not parse malformed var() declarations', function() { - return expect(testPlugin( - './test/fixtures/malformed-variable-usage.css', - './test/fixtures/malformed-variable-usage.expected.css' - ) - ).to.eventually.be.rejected; - }); - - describe('rule clean up', function() { - test( - 'should clean up rules if we removed variable declarations to make it empty', - 'remove-empty-rules-after-variable-collection' - ); - test( - 'should clean up neseted rules if we removed variable declarations to make it empty', - 'remove-nested-empty-rules-after-variable-collection' - ); - }); +describe("postcss-css-variables", function() { + // Just make sure it doesn't mangle anything + test( + "should work when there are no var() functions to consume declarations", + "no-var-func" + ); + test( + "should work when there are no var() functions(just `:root`) to consume declarations", + "no-var-func-just-root" + ); + test("should work when no variable name passed to `var()`", "empty-var-func"); + + test("should work with variables declared in root", "root-variable"); + test( + "should work with variables with parenthesis in fallback", + "fallback-with-parenthesis" + ); + + test( + "should work with locally scoped variable in a non-root rule", + "local-variable-non-root" + ); + + test( + "should work with any combinator selector if the last piece is the variable we have in the map", + "scope-last-piece-of-combinator-sequence" + ); + + test( + 'should work with descendant selector type "nesting"', + "descendant-selector" + ); + test( + 'should work with css4 descendant selector type "nesting"', + "css4-descendant-selector" + ); + test( + "should work with direct descendant selector", + "direct-descendant-selector" + ); + + test( + "should work with direct descendant selector where variables are scoped in a descendant selector", + "direct-descendant-selector-descendant-scope" + ); + test( + "should work with direct descendant selector where variables are scoped in a direct descendant selector", + "direct-descendant-selector-direct-descendant-scope" + ); + + test("should work with pseudo selectors", "pseudo-selector"); + //test('should work with multiple pseudo selectors', 'pseudo-multi'); + test( + "should work with variables declared in pseudo selectors", + "pseudo-selector-declare-variable" + ); + + test( + "should work with variables defined in comma separated selector", + "comma-separated-variable-declaration" + ); + + test( + "should work use the correct variable in comma separated selector", + "comma-separated-variable-usage" + ); + + test("should work with star selector", "star-selector-scope"); + + test( + "should work with `!important` variable declarations", + "important-variable-declaration" + ); + + describe("with at-rules", function() { + test("should add rule declaration of property in @media", "media-query"); + test( + "should add rule declaration of property in @support", + "support-directive" + ); + + test( + "should work with @media, preserving rule order", + "media-query-preserve-rule-order", + { preserveAtRulesOrder: true } + ); + + test("should work with nested @media", "media-query-nested", { + preserveAtRulesOrder: false + }); + test( + "should work with nested @media, preserving rule order", + "media-query-nested-preserver-rule-order", + { preserveAtRulesOrder: true } + ); + + test("should cascade to nested rules", "cascade-on-nested-rules"); + + test( + "should cascade with calc-expression to nested rules", + "cascade-with-calc-expression-on-nested-rules" + ); + + test( + "should cascade to nested rules in the proper scope. See issue #2", + "cascade-on-nested-rules-in-proper-scope" + ); + }); + + test( + "should work with variables that reference other variables", + "variable-reference-other-variable" + ); + + test( + "should work with variable with calc-expression that reference other variables", + "variable-with-calc-expression-reference-other-variable" + ); + + test( + "should work with variables that reference other variables with at-rule changing the value", + "variable-reference-other-variable-media-query1" + ); + test( + "should work with local variables that reference other variables with at-rule changing the value", + "variable-reference-other-variable-media-query2" + ); + + test( + "should work with variables that try to self reference", + "self-reference" + ); + test( + "should work with variables that try to self reference and fallback properly", + "self-reference-fallback" + ); + test("should work with circular reference", "circular-reference"); + + describe("with `options.variables`", function() { + test("should work with JS defined variables", "js-defined", { + variables: MOCK_JS_VARIABLES + }); + test( + "should work with JS defined important variables", + "js-defined-important", + { variables: MOCK_JS_VARIABLES } + ); + test( + "should preserve -- declarations and var() values with `options.variables` AND `options.preserve`", + "js-defined-preserve", + { + variables: MOCK_JS_VARIABLES, + preserve: true + } + ); + test( + "should preserve var() values and clean injected declarations with `options.variables` AND `options.preserve` AND `options.preserveInjectedVariables: false`", + "js-defined-preserve-injected", + { + variables: MOCK_JS_VARIABLES, + preserve: true, + preserveInjectedVariables: false + } + ); + test( + "should cast non-string values to string", + "js-defined-non-string-values-casted-to-string", + { + variables: NON_STRING_VARIABLES + } + ); + }); + + describe("with `options.preserve`", function() { + test( + "preserves variables when `preserve` is `true`", + "preserve-variables", + { preserve: true } + ); + + test( + "preserves variables in @media when `preserve` is `true`", + "preserve-variables-in-media", + { preserve: true } + ); + + test( + "preserves computed value when `preserve` is `'computed'`", + "preserve-computed", + { preserve: "computed" } + ); + + test( + "preserves variables when `preserve` function applies", + "preserve-variables-conditionally", + { + preserve: function(declaration) { + return !( + declaration.prop.includes("--no-preserve") || + declaration.value.includes("--no-preserve") + ); + } + } + ); + }); + + describe("missing variable declarations", function() { + test("should work with missing variables", "missing-variable-usage"); + test( + "should use fallback value if provided with missing variables", + "missing-variable-should-fallback" + ); + it("should use string values for `undefined` values, see #22", function() { + return fs + .readFileAsync("./test/fixtures/missing-variable-usage.css", "utf8") + .then(function(buffer) { + var contents = String(buffer); + return postcss([cssvariables()]) + .process(contents) + .then(function(result) { + var root = result.root; + var fooRule = root.nodes[0]; + expect(fooRule.selector).to.equal(".box-foo"); + var colorDecl = fooRule.nodes[0]; + expect(colorDecl.value).to.be.a("string"); + expect(colorDecl.value).to.be.equal("undefined"); + + expect(result.warnings().length).to.be.equal(1); + expect(result.warnings()[0].type).to.be.equal("warning"); + expect(result.warnings()[0].text).to.be.equal( + "variable --missing is undefined and used without a fallback" + ); + }); + }); + }); + test( + "should use fallback variable if provided with missing variables", + "missing-variable-should-fallback-var" + ); + test( + "should use fallback variable if provided with missing variables calc", + "missing-variable-should-fallback-calc" + ); + test( + "should use fallback variable if provided with missing variables nested", + "missing-variable-should-fallback-nested" + ); + test( + "should not mangle outer function parentheses", + "nested-inside-other-func" + ); + test( + "should not mangle outer function parentheses - with fallback", + "nested-inside-other-func-with-fallback" + ); + test( + "should not mangle outer function parentheses - calc", + "nested-inside-calc-func" + ); + test( + "should not mangle outer function parentheses - calc with fallback", + "nested-inside-calc-func-with-fallback" + ); + test( + "should not mangle outer function parentheses - calc with fallback var()", + "nested-inside-calc-func-with-fallback-var" + ); + }); + + test( + "should accept whitespace in var() declarations", + "whitespace-in-var-declaration" + ); + + it("should not parse malformed var() declarations", function() { + return expect( + testPlugin( + "./test/fixtures/malformed-variable-usage.css", + "./test/fixtures/malformed-variable-usage.expected.css" + ) + ).to.eventually.be.rejected; + }); + + describe("rule clean up", function() { + test( + "should clean up rules if we removed variable declarations to make it empty", + "remove-empty-rules-after-variable-collection" + ); + test( + "should clean up neseted rules if we removed variable declarations to make it empty", + "remove-nested-empty-rules-after-variable-collection" + ); + }); });