Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

postcss-params

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

postcss-params

Parse media-query-like params into PostCSS plugins

  • 1.0.2
  • latest
  • npm
  • Socket score

Version published
Weekly downloads
19
increased by58.33%
Maintainers
1
Weekly downloads
 
Created
Source

npm version Build Status Coverage Status Code Climate dependencies Status devDependencies Status

PostCSS Params

postcss-params has two usage modes:

  1. Target devices/clients based on a build configuration, much like media queries.
  2. Pass strings from css to your postcss plugin, using a familiar syntax.

Some sites serve different assets to different clients. For example, you may have some IE-specific css hacks that you only want to serve to IE browsers. Or you want to load certain fonts for certain countries. PostCSS is a good way to keep all your code in one file, then generate separate assets.

@my-plugin (browser: ie) {
  button {
    background-color: red;
  }
}
@my-plugin not (browser: ie) {
  button {
    background-color: green;
  }
}

postcss-params helps you write a plugin which reads the (browser: ie) parameter string, and keep or discard the block accordingly.

Build two assets, with configurations {browser: ie} and {}. PostCSS will generate two assets. One you can serve to your locked-in customers browsing from their lunch breaks at BigCorp. The other asset you can serve to the rest of the civilized world.


buildComparator

buildComparator accepts a param string and returns a function. The resulting comparator function accepts a configuration object, and returns true if the params match the configuration, and false otherwise.

See the tests in tests/buildComparator for more examples.

CSS:

@my-plugin (region: cn) {
  body {
    font-family: ".PingFang-SC-Regular", sans-serif;
  }
}
@my-plugin not (region: cn) {
  body {
    font-family: "Helvetica Neue", Arial, sans-serif !default;
  }
}

Plugin:

const { buildComparator } = require('postcss-params');
postcss.plugin('my-plugin', (configuration) => (root) => {
  root.walkAtRules('my-plugin', (atRule) => {
    const comparator = buildComparator(atRule.params);
    if (comparator(configuration)) {
      atRule.replaceWith(atRule.nodes);
    } else {
      atRule.remove();
    }
  });
});

Running postcss with various configuration objects will result in css assets suitable for separate intended audiences. For example, you may serve a different font-family in China, but not want to load this asset for all countries.

configuration is provided to PostCSS through your build tool.

Example configuration:

{
  debug:  true,
  region: "us",
  theme:  "blue"
}

buildAst

buildAst gives you finer control and access to the params written in css.

Given this simple rule:

@my-plugin (theme: red) {
  body {
    background-color: theme-color;
  }
}

and this plugin:

const { buildAst } = require('postcss-params');
postcss.plugin('my-plugin', (configuration) => (root) => {
  root.walkAtRules('my-plugin', (atRule) => {
    const ast = buildAst(atRule.params);
    console.log(ast);
  });
});

This AST is generated:

{ feature: "theme", value: "red" }

Given this more complicated rule:

@my-plugin (debug),
           (region: cn) and (theme: red),
           (region: us) and (theme: blue),
           not (production) and (staging) {
  body {
    background-color: red;
  }
}

Plugin:

const { buildAst } = require('postcss-params');
postcss.plugin('my-plugin', (configuration) => (root) => {
  root.walkAtRules('my-plugin', (atRule) => {
    const ast = buildAst(atRule.params);
    console.log(ast);
  });
});

This AST is generated:

any
├─ { feature: debug, value: true }
├─ all
│  ├─ { feature: region, value: cn }
│  └─ { feature: theme, value: red }
├─ all
│  ├─ { feature: region, value: us }
│  └─ { feature: theme, value: blue }
└─ all
   ├─ { feature: production, not: true }
   └─ { feature: staging, not: true }

Now your PostCSS plugin can make use of this AST to pull values into variables, or decide to keep or discard the block.

Limitations:

  • Feature names and values must NOT contain characters (),:
  • Errors are fairly opaque ('Expected L_PAREN')
  • Feature values are optional, so comparators must expect String or Undefined
  • Feature can only have one value. (f: a), (f: b) can't be (f: a, b)
    • As a hack, (f: a|b) is legal, and the comparator can split on |

AST Object Reference

The resulting AST from buildAst is a tree structure. There are three possible nodes in this tree:

  • any: if any item resolves true, return true.

  • all: if all items resolve true, return true.

  • feature: compare the param value with the config value.

    • not: if feature returns true, return false. And vice-versa.

    Given this contrived rule:

    @my-plugin (debug),
               (region: cn) and (theme: red),
               (region: us) and (theme: blue),
               not (production) and (staging) {
      body {
        background-color: red;
      }
    }
    

This AST is generated:

any
├─ { feature: debug, value: true }
├─ all
│  ├─ { feature: region, value: cn }
│  └─ { feature: theme, value: red }
├─ all
│  ├─ { feature: region, value: us }
│  └─ { feature: theme, value: blue }
└─ all
   ├─ { feature: production, not: true }
   └─ { feature: staging, not: true }

See the tests in tests/buildAst for more examples.


About the syntax

All PostCSS at-rules follow the structure @plugin-name params { body }. Plugins are given the params as a string, with no provisions for parsing, or even a standard format for clients to write params.

CSS already has an analogous structure for media queries:

  • @media media-query [, media-query]* { rule-list }

where media-query can take either form:

  • [NOT|ONLY]? media-type [AND (media-feature[: value]?)]*
  • (media-feature[: value]?) [AND (media-feature[: value]?)]*

However, this has some limitations:

  1. media-type is not meant for general-purpose use. It accepts specific values, e.g., all, screen, print. We are only interested in media-feature usage.
  2. NOT must be used with media-type. (NOT (media-feature) is illegal).
  3. ONLY was a hack for older browsers.

So we have defined a similar grammar which:

  1. removes media-type entirely.
  2. allows NOT to be juxtaposed with media-feature.
  3. removes ONLY.

ASTs are automatically flattened

any and all nodes with a single child are replaced with that child node.

any
└─ all
   └─ { feature: debug, value: true }

is the same as

{ feature: debug, value: true }

LL(1) Grammar Reference - Please do not LL(2+)

CommaSeparatedList
 : MediaQuery [ COMMA MediaQuery ]*
MediaQuery
 : [NOT]? Feature [ AND Feature ]*
Feature
 : L_PAREN IDENT [ COLON IDENT ]? R_PAREN

FAQs

Package last updated on 04 Sep 2017

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc