Coder Social home page Coder Social logo

deliriousrhino / css-vars-ponyfill Goto Github PK

View Code? Open in Web Editor NEW

This project forked from jhildenbiddle/css-vars-ponyfill

0.0 2.0 0.0 665 KB

A ponyfill that provides client-side support for CSS custom properties (aka "CSS variables") in legacy browsers

Home Page: https://codepen.io/jhildenbiddle/pen/ZxYJrR/

License: MIT License

JavaScript 100.00%

css-vars-ponyfill's Introduction

css-vars-ponyfill

NPM Build Status Codacy Codecov License: MIT Tweet

A ponyfill that provides client-side support for CSS custom properties (aka "CSS variables") in legacy browsers.



Features

  • Client-side transformation of CSS custom properties to static values
  • Live updates of runtime values in both modern and legacy browsers
  • Auto-updates on <link> and <style> changes
  • Transforms <link>, <style>, and @import CSS
  • Transforms relative url() paths to absolute URLs
  • Supports chained custom property references
  • Supports complex values
  • Supports fallback values
  • UMD and ES6 module available
  • Lightweight (5k min+gzip) and dependency-free

Limitations

  • Custom property support is limited to :root declarations
  • The use of var() is limited to property values (per W3C specification)

Browser Support

IE Edge Chrome Firefox Safari
9+ 12+ 19+ 6+ 6+

Installation

NPM:

npm install css-vars-ponyfill
// file.js
import cssVars from 'css-vars-ponyfill';
cssVars({
  // ...
});

Git:

git clone https://github.com/jhildenbiddle/css-vars-ponyfill.git

CDN (unpkg.com shown, also on jsdelivr.net):

<!-- file.html (latest v1.x.x) -->
<script src="https://unpkg.com/css-vars-ponyfill@1"></script>
<script>
  cssVars({
    // ...
  });
</script>

Examples

HTML / CSS:

<!-- file.html -->

<link rel="stylesheet" href="style.css">
<style>
  :root {
    --color: black;
  }
</style>
/* style.css */

:root {
  /* Chained references */
  --a: var(--b);
  --b: var(--c);
  --c: 10px;
}

div {
  /* External value (from <style>) */
  color: var(--color);

  /* Fallback */
  margin: var(--unknown, 20px);

  /* Complex value */
  padding: calc(2 * var(--a));
}

JavaScript (see Options):

import cssVars from 'css-vars-ponyfill';

// Call using defaults
cssVars();

// Or call with options
cssVars({
  // ...
});

The ponyfill will:

  1. Get the <link>, <style>, and @import CSS
  2. Parse the CSS and convert it to an abstract syntax tree
  3. Transform CSS custom properties to static values
  4. Transforms relative url() paths to absolute URLs
  5. Convert the AST back to CSS
  6. Append legacy-compatible CSS to the DOM
<style id="css-vars-ponyfill">
  div {
    color: black;
    margin: 20px;
    padding: calc(2 * 10px);
  }
</style>

To update values, call cssVars() with options.variables:

cssVars({
  variables: {
    color: 'red',
    unknown: '5px'
  }
});

Values will be updated in both legacy and modern browsers:

  • In legacy browsers, the ponyfill will get, parse, transform, and append legacy-compatible CSS to the DOM once again.

    <style id="css-vars-ponyfill">
      div {
        color: red;
        margin: 5px;
        padding: calc(2 * 10px);
      }
    </style>
  • In modern browsers with native support for CSS custom properties, the ponyfill will update values using the style.setProperty() interface.

    document.documentElement.style.setProperty('--color', 'red');
    document.documentElement.style.setProperty('--unknown', '5px');

Options

Example

// Default values shown
cssVars({
  include      : 'link[rel=stylesheet],style',
  exclude      : '',
  fixNestedCalc: true,
  onlyLegacy   : true,
  onlyVars     : false,
  preserve     : false,
  silent       : false,
  updateDOM    : true,
  updateURLs   : true,
  variables    : {
    // ...
  },
  watch        : false,
  onBeforeSend(xhr, node, url) {
    // ...
  },
  onSuccess(cssText, node, url) {
    // ...
  },
  onWarning(message) {
    // ...
  },
  onError(message, node, xhr, url) {
    // ...
  },
  onComplete(cssText, styleNode) {
    // ...
  }
});

options.include

  • Type: string
  • Default: "link[rel=stylesheet],style"

CSS selector matching <link rel="stylesheet"> and <style> nodes to process. The default value includes all style and link nodes.

Tip: The default value is the safest setting, but it is not necessarily the fastest. For the best performance, avoid unnecessary CSS processing by including only CSS that needs to be ponyfilled. See options.exclude for an alternate approach.

Example

// Example 1: Include local CSS only
cssVars({
  // Include only CSS from <style> nodes and <link> nodes
  // with an href that does not contain "//"
  include: 'style,link[rel="stylesheet"]:not([href*="//"])'
});

// Example 2: Include via data attribute
cssVars({
  // Include ony CSS from <link> and <style> nodes with
  // a "data-cssvarsponyfill" attribute set to "true"
  // Ex: <link data-cssvarsponyfill="true" rel="stylesheet" href="...">
  // Ex: <style data-cssvarsponyfill="true">...</style>
  include: '[data-cssvarsponyfill="true"]'
});

options.exclude

  • Type: string
  • Default: none

CSS selector matching <link rel="stylesheet"> and <style> nodes to exclude from those matched by options.include.

Tip: The default value is the safest setting, but it is not necessarily the fastest. For the best performance, avoid unnecessary CSS processing by excluding CSS that does not need to be ponyfilled. See options.include for an alternate approach.

Example

// Example 1: Exclude based on <link> href
cssVars({
  // Of the matched 'include' nodes, exclude any node
  // with an href that contains "bootstrap"
  exclude: '[href*=bootstrap]'
});

// Example 2: Exclude via data attribute
cssVars({
  // Of the matched 'include' nodes, exclude any node
  // with a "data-cssvarsponyfill" attribute set to "false"
  // Ex: <link data-cssvarsponyfill="false" rel="stylesheet" href="...">
  // Ex: <style data-cssvarsponyfill="false">...</style>
  include: '[data-cssvarsponyfill="false"]'
});

options.fixNestedCalc

  • Type: boolean
  • Default: true

Determines if nested calc keywords will be removed for compatibility with legacy browsers.

Example

CSS:

:root {
  --a: calc(1px + var(--b));
  --b: calc(2px + var(--c));
  --c: calc(3px + var(--d));
  --d: 4px;
}
p {
  margin: var(--a);
}

JavaScript:

cssVars({
  fixNestedCalc: true // default
});

Output when fixNestedCalc: true

p {
  /* Works in legacy browsers */
  margin: calc(1px + (2px + (3px + 4)));
}

Output when fixNestedCalc: false

p {
  /* Does not work in legacy browsers */
  margin: calc(1px + calc(2px + calc(3px + 4)));
}

options.onlyLegacy

  • Type: boolean
  • Default: true

Determines if the ponyfill will only generate legacy-compatible CSS in browsers that lack native support (i.e., legacy browsers).

When true, the ponyfill will only generate legacy-compatible CSS, trigger callbacks, and (optionally) update the DOM in browsers that lack native support. When false, the ponyfill will treat all browsers as legacy, regardless of their support for CSS custom properties.

Example

cssVars({
  onlyLegacy: true // default
});

options.onlyVars

  • Type: boolean
  • Default: false

Determines if CSS rulesets and declarations without a custom property value should be removed from the transformed CSS.

When true, rulesets and declarations without a custom property value will be removed from the generated CSS, reducing CSS output size. This can significantly reduce the amount of CSS processed and output by the ponyfill, but runs the risk of breaking the original cascade order once the transformed values are appended to the DOM. When false, all rulesets and declarations will be retained in the generated CSS. This means the ponyfill will process and output more CSS, but it ensures that the original cascade order is maintained after the transformed styles are appended to the DOM.

Note: @font-face and @keyframes require all declarations to be retained if a CSS custom property is used anywhere within the ruleset.

Example

CSS:

:root {
  --color: red;
}
h1 {
  font-weight: bold;
}
p {
  margin: 20px;
  padding: 10px;
  color: var(--color);
}

JavaScript:

cssVars({
  onlyVars: true // default
});

Output when onlyVars: true

p {
  color: red;
}

Output when onlyVars: false

h1 {
  font-weight: bold;
}
p {
  margin: 20px;
  padding: 10px;
  color: red;
}

options.preserve

  • Type: boolean
  • Default: false

Determines if the original CSS custom property declaration will be retained in the transformed CSS.

When true, the original custom property declarations are available in the transformed CSS along with their static values. When false, only static values are available in the transformed CSS.

Example

CSS:

:root {
  --color: red;
}
p {
  color: var(--color);
}

JavaScript:

cssVars({
  preserve: false // default
});

Output when preserve: false

p {
  color: red;
}

Output when preserve: true

:root {
  --color: red;
}
p {
  color: red;
  color: var(--color);
}

options.silent

  • Type: boolean
  • Default: false

Determines if warning and error messages will be displayed on the console.

When true, messages will be displayed on the console for each warning and error encountered while processing CSS. When false, messages will not be displayed on the console but will still be available using the options.onWarning and options.onSuccess callbacks.

Example

CSS:

@import "fail.css"

p {
  color: var(--fail);
}

p {
  color: red;

JavaScript:

cssVars({
  silent: false // default
});

Console:

> CSS XHR error: "fail.css" 404 (Not Found)
> CSS transform warning: variable "--fail" is undefined
> CSS parse error: missing "}"

options.updateDOM

  • Type: boolean
  • Default: true

Determines if the ponyfill will update the DOM after processing CSS custom properties.

When true, the ponyfill updates will be applied to the DOM. For legacy browsers, this is accomplished by appending a <style> node with transformed CSS after the last <link> or <style> node processed. For modern browsers, options.variables values will be applied as custom property changes using the native style.setProperty() method. When false, the DOM will not be updated by the polyfill in either modern or legacy browsers, but transformed CSS can be accessed with either the options.onSuccess or options.onComplete callback.

Example

HTML:

<head>
  <title>Title</title>
  <link rel="stylesheet" href="style.css">
</head>

JavaScript:

cssVars({
  updateDOM: true // default
});

Result when updateDOM: true

<head>
  <title>Title</title>
  <link rel="stylesheet" href="style.css">
  <style id="css-vars-ponyfill">
    /* Transformed CSS ... */
  </style>
</head>

options.updateURLs

  • Type: boolean
  • Default: true

Determines if the ponyfill will convert relative url() paths to absolute urls.

When true, the ponyfill will parse each block of external CSS for relative url() paths and convert them to absolute URLs. This allows resources (images, fonts, etc.) referenced using paths relative to an external stylesheet to load properly when legacy-compatible CSS is generated and appended to the DOM by the ponyfill. When false, the ponyfill will not modify relative url() paths.

Example

CSS:

/* http://mydomain.com/path/to/style.css */

div {
  background-image: url(image.jpg);
}

JavaScript:

cssVars({
  updateURLs: true // default
});

Output when updateURLs: true

div {
  background-image: url(http://mydomain.com/path/to/image.jpg);
}

Output when updateURLs: false

div {
  background-image: url(image.jpg);
}

options.variables

  • Type: object
  • Default: {}

A map of custom property name/value pairs. Property names can omit or include the leading double-hyphen (--), and values specified will override previous values.

Legacy browsers will process these values while generating legacy-compatible CSS. Modern browsers with native custom property support will apply these values using the native setProperty() method when options.updateDOM is true.

Example

cssVars({
  variables: {
    color1    : 'red',
    '--color2': 'green'
  }
});

options.watch

  • Type: boolean
  • Default: false

Determines if a MutationObserver will be created to watch for <link> and <style> DOM mutations.

When true, the ponyfill will call itself when a <link> or <style> node is added, removed, or has its disabled or href attribute modified. The settings used will be the same as those passed to the ponyfill the first time options.watch was set to true.

Note that this feature requires either native support for MutationObserver or a polyfill for legacy browsers.

Example

cssVars({
  watch: false // default
});

options.onBeforeSend

  • Type: function
  • Arguments:
    1. xhr: The XHR object containing details of the failed request
    2. node: The source node object reference
    3. url: The source URL string (<link> href or @import url)

Callback before each XMLHttpRequest (XHR) is sent. Allows modifying the XML object by setting properties, calling methods, or adding event handlers.

Example

cssVars({
  onBeforeSend(xhr, node, url) {
    // Domain-specific XHR settings
    if (/some-domain.com/.test(url)) {
      xhr.withCredentials = true;
      xhr.setRequestHeader("foo", "1");
      xhr.setRequestHeader("bar", "2");
    }
  }
});

options.onSuccess

  • Type: function
  • Arguments:
    1. cssText: A string of CSS text from node and url
    2. node: The source node object reference
    3. url: The source URL string (<link> href, @import url, or page url for <style> data)

Callback after CSS data has been collected from each node and before CSS custom properties have been transformed. Allows modifying the CSS data before it is transformed by returning any string value (or false to skip).

Note that the order in which <link> and @import CSS data is "successfully" collected (thereby triggering this callback) is not guaranteed as these requests are asynchronous.

Example

cssVars({
  onSuccess(cssText, node, url) {
    // Replace all instances of "color: red" with "color: blue"
    const newCssText = cssText.replace(/color:\s*red\s;/g, 'color: blue;');

    return newCssText;
  }
});

options.onWarning

  • Type: function
  • Arguments:
    1. message: The warning message

Callback after each CSS parsing warning has occurred.

Example

CSS:

p {
  color: var(--fail);
}

JavaScript:

cssVars({
  onWarning(message) {
    console.log(message); // 1
  }
});

// 1 => 'CSS transform warning: variable "--fail" is undefined'

options.onError

  • Type: function
  • Arguments:
    1. message: The error message
    2. node: The source node object reference
    3. xhr: The XHR object containing details of the failed request
    4. url: The source URL string (<link> href or @import url)

Callback after a CSS parsing error has occurred or an XHR request has failed.

Example

HTML:

<link rel="stylesheet" href="path/to/fail.css">

JavaScript:

cssVars({
  onError(message, node, xhr, url) {
    console.log(message); // 1
    console.log(node); // 2
    console.log(xhr.status); // 3
    console.log(xhr.statusText); // 4
    console.log(url); // 5
  }
});

// 1 => 'CSS XHR error: "http://domain.com/path/to/fail.css" 404 (Not Found)'
// 2 => <link rel="stylesheet" href="path/to/fail.css">
// 3 => '404'
// 4 => 'Not Found'
// 5 => 'http://domain.com/path/to/fail.css'

options.onComplete

  • Type: function
  • Arguments:
    1. cssText: A string of concatenated CSS text from all nodes in DOM order
    2. styleNode: An object reference to the appended <style> node

Callback after all CSS has been processed, legacy-compatible CSS has been generated, and (optionally) the DOM has been updated.

Example

cssVars({
  onComplete(cssText, styleNode) {
    // ...
  }
});

Attribution

This ponyfill includes code based on the following projects. Many thanks to the authors and contributors for helping to make this project possible.

Contact

License

This project is licensed under the MIT License. See the MIT LICENSE for details.

Copyright (c) 2018 John Hildenbiddle (@jhildenbiddle)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.