Ie11 - Does a Polyfill/Script Exist For CSS Variables

IE11 - does a polyfill / script exist for CSS variables?

Yes, so long as you're processing root-level custom properties (IE9+).

  • GitHub: https://github.com/jhildenbiddle/css-vars-ponyfill
  • NPM: https://www.npmjs.com/package/css-vars-ponyfill
  • Demo: https://codepen.io/jhildenbiddle/pen/ZxYJrR

From the README:

Features

  • Client-side transformation of CSS custom properties to static values
  • Live updates of runtime values in both modern and legacy browsers
  • Transforms <link>, <style>, and @import CSS
  • Transforms relative url() paths to absolute URLs
  • Supports chained and nested var() functions
  • Supports var() function fallback values
  • Supports web components / shadow DOM CSS
  • Watch mode auto-updates on <link> and <style> changes
  • UMD and ES6 module available
  • TypeScript definitions included
  • Lightweight (6k min+gzip) and dependency-free

Limitations

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

Here are a few examples of what the library can handle:

Root-level custom properties

:root {
--a: red;
}

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

Chained custom properties

:root {
--a: var(--b);
--b: var(--c);
--c: red;
}

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

Nested custom properties

:root {
--a: 1em;
--b: 2;
}

p {
font-size: calc(var(--a) * var(--b));
}

Fallback values

p {
font-size: var(--a, 1rem);
color: var(--b, var(--c, var(--d, red)));
}

Transforms <link>, <style>, and @import CSS

<link rel="stylesheet" href="/absolute/path/to/style.css">
<link rel="stylesheet" href="../relative/path/to/style.css">

<style>
@import "/absolute/path/to/style.css";
@import "../relative/path/to/style.css";
</style>

Transforms web components / shadow DOM

<custom-element>
#shadow-root
<style>
.my-custom-element {
color: var(--test-color);
}
</style>
<div class="my-custom-element">Hello.</div>
</custom-element>

For the sake of completeness: w3c specs

Hope this helps.

(Shameless self-promotion: Check)

CSS custom properties polyfill for ie11

You didn't mention how you're bundling your JavaScript, but yes, it's possible. For example, PostCSS has a plugin, which polyfills this feature.

The usage depends on how you're bundling your script files. With Webpack, for example, you'd define this plugin in your postcss config or import it as a plugin under your webpack config:

// webpack.config.js:
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader", "postcss-loader"]
}
]
}
}

// postcss.config.js
module.exports = {
plugins: [
require('postcss-custom-properties'),
require('autoprefixer'),
// any other PostCSS plugins
]
}

The plugin also has an example for programmatic usage (as a separate node script):

// dependencies
var fs = require('fs')
var postcss = require('postcss')
var customProperties = require('postcss-custom-properties')

// css to be processed
var css = fs.readFileSync('input.css', 'utf8')

// process css using postcss-custom-properties
var output = postcss()
.use(customProperties())
.process(css)
.css

Can autoprefixer solve the issue of CSS variables for Internet Explorer?

According to caniuse.com, of current browsers only IE, Edge (older versions) and Opera Mini do not support CSS variables. This polyfil appears to work on all three really well.

This is an attempt at a very basic CSS variables (custom properties) polyfil. In reality this is more of a partial polyfill as it will not cover variables inside of variables, DOM scoping or anything else "fancy". Just taking variables declared anywhere in the CSS and then re-parsing the CSS for var() statements and replacing them in browsers that don't natively support CSS variables.

I try to test this polyfil in IE 11 and looks like it is working with it.

/*! * css-var-polyfill.js - v1.0.0 * * Copyright (c) 2018 Aaron Barker <http://aaronbarker.net> * Released under the MIT license * * Date: 2018-03-09 */let cssVarPoly = {  init: function() {    // first lets see if the browser supports CSS variables    // No version of IE supports window.CSS.supports, so if that isn't supported in the first place we know CSS variables is not supported    // Edge supports supports, so check for actual variable support    if (window.CSS && window.CSS.supports && window.CSS.supports('(--foo: red)')) {      // this browser does support variables, abort      console.log('your browser supports CSS variables, aborting and letting the native support handle things.');      return;    } else {      // edge barfs on console statements if the console is not open... lame!      console.log('no support for you! polyfill all (some of) the things!!');      document.querySelector('body').classList.add('cssvars-polyfilled');    }
cssVarPoly.ratifiedVars = {}; cssVarPoly.varsByBlock = {}; cssVarPoly.oldCSS = {}; // start things off cssVarPoly.findCSS(); cssVarPoly.updateCSS(); }, // find all the css blocks, save off the content, and look for variables findCSS: function() { let styleBlocks = document.querySelectorAll('style:not(.inserted),link[rel="stylesheet"]');
// we need to track the order of the style/link elements when we save off the CSS, set a counter let counter = 1;
// loop through all CSS blocks looking for CSS variables being set [].forEach.call(styleBlocks, function(block) { // console.log(block.nodeName); let theCSS; if (block.nodeName === 'STYLE') { // console.log("style"); theCSS = block.innerHTML; cssVarPoly.findSetters(theCSS, counter); } else if (block.nodeName === 'LINK') { // console.log("link"); cssVarPoly.getLink(block.getAttribute('href'), counter, function(counter, request) { cssVarPoly.findSetters(request.responseText, counter); cssVarPoly.oldCSS[counter] = request.responseText; cssVarPoly.updateCSS(); }); theCSS = ''; } // save off the CSS to parse through again later. the value may be empty for links that are waiting for their ajax return, but this will maintain the order cssVarPoly.oldCSS[counter] = theCSS; counter++; }); },
// find all the "--variable: value" matches in a provided block of CSS and add them to the master list findSetters: function(theCSS, counter) { // console.log(theCSS); cssVarPoly.varsByBlock[counter] = theCSS.match(/(--.+:.+;)/g) || []; },
// run through all the CSS blocks to update the variables and then inject on the page updateCSS: function() { // first lets loop through all the variables to make sure later vars trump earlier vars cssVarPoly.ratifySetters(cssVarPoly.varsByBlock);
// loop through the css blocks (styles and links) for (let curCSSID in cssVarPoly.oldCSS) { // console.log("curCSS:",oldCSS[curCSSID]); let newCSS = cssVarPoly.replaceGetters(cssVarPoly.oldCSS[curCSSID], cssVarPoly.ratifiedVars); // put it back into the page // first check to see if this block exists already if (document.querySelector('#inserted' + curCSSID)) { // console.log("updating") document.querySelector('#inserted' + curCSSID).innerHTML = newCSS; } else { // console.log("adding"); var style = document.createElement('style'); style.type = 'text/css'; style.innerHTML = newCSS; style.classList.add('inserted'); style.id = 'inserted' + curCSSID; document.getElementsByTagName('head')[0].appendChild(style); } }; },
// parse a provided block of CSS looking for a provided list of variables and replace the --var-name with the correct value replaceGetters: function(curCSS, varList) { // console.log(varList); for (let theVar in varList) { // console.log(theVar); // match the variable with the actual variable name let getterRegex = new RegExp('var\\(\\s*' + theVar + '\\s*\\)', 'g'); // console.log(getterRegex); // console.log(curCSS); curCSS = curCSS.replace(getterRegex, varList[theVar]);
// now check for any getters that are left that have fallbacks let getterRegex2 = new RegExp('var\\(\\s*.+\\s*,\\s*(.+)\\)', 'g'); // console.log(getterRegex); // console.log(curCSS); let matches = curCSS.match(getterRegex2); if (matches) { // console.log("matches",matches); matches.forEach(function(match) { // console.log(match.match(/var\(.+,\s*(.+)\)/)) // find the fallback within the getter curCSS = curCSS.replace(match, match.match(/var\(.+,\s*(.+)\)/)[1]); });
}
// curCSS = curCSS.replace(getterRegex2,varList[theVar]); }; // console.log(curCSS); return curCSS; },
// determine the css variable name value pair and track the latest ratifySetters: function(varList) { // console.log("varList:",varList); // loop through each block in order, to maintain order specificity for (let curBlock in varList) { let curVars = varList[curBlock]; // console.log("curVars:",curVars); // loop through each var in the block curVars.forEach(function(theVar) { // console.log(theVar); // split on the name value pair separator let matches = theVar.split(/:\s*/); // console.log(matches); // put it in an object based on the varName. Each time we do this it will override a previous use and so will always have the last set be the winner // 0 = the name, 1 = the value, strip off the ; if it is there cssVarPoly.ratifiedVars[matches[0]] = matches[1].replace(/;/, ''); }); }; // console.log(ratifiedVars); },
// get the CSS file (same domain for now) getLink: function(url, counter, success) { var request = new XMLHttpRequest(); request.open('GET', url, true); request.overrideMimeType('text/css;'); request.onload = function() { if (request.status >= 200 && request.status < 400) { // Success! // console.log(request.responseText); if (typeof success === 'function') { success(counter, request); } } else { // We reached our target server, but it returned an error console.warn('an error was returned from:', url); } };
request.onerror = function() { // There was a connection error of some sort console.warn('we could not get anything from:', url); };
request.send(); }};
cssVarPoly.init();
:root {  --externalcolor: red;  --samename: orange;  --samename: #0f0;  --foo: green;  --FOO: #0f0;  --halfsuccess: orange;  --success: green;  --success2: #0f0;}html {  font-family: var(--fontsans);}.success {  color: green;}
.fail { color: red;}
span { display: inline-block; margin: 5px;}
.samename { color: var(--samename);}
.demo1 { color: #f00; color: var(--success);}
.demo2 { color: #f00; color: var( --success2);}
.demo3 { color: #f00; color: var(--halfsuccess); color: var(--success);}
.demo4 { color: red; border-color: #f00;}
.inlineoverlink { color: #f00;}
p { padding: var(--spacing-l);}
.lower { color: var(--foo);}
.upper { color: var(--FOO);}
.externalcolor { color: var(--externalcolor);}
.fallback { color: #f00; color: var(--wrongname, green);}
// for the top documentation.supports { color: green; .no { display:none; }}.showforpolyfill { display:none;}
.cssvars-polyfilled { .supports { color: red; .no { display:inline; } } .showforpolyfill { display:inline; } .hideforpolyfill { display:none; }} .hide,.hide-the-docs .documentation { display:none;}/* declare some font-family stuff at bottom of file to reflect on stuff above it*/
:root { --fontsans: arial;}
<!-- Copy below for codepen update -->    <h1>CSS Variables Polyfill</h1>    <p>This is now managed (and available for PRs) at <a href="https://github.com/aaronbarker/css-variables-polyfill">https://github.com/aaronbarker/css-variables-polyfill</a>.</p>    <p>      This is an attempt at a very basic <a href="https://drafts.csswg.org/css-variables/">CSS variables (custom properties)</a> polyfil. In reality this is more of a <em>partial</em> polyfill as it will not cover variables inside of variables, DOM scoping or anything else "fancy". Just taking variables declared anywhere in the CSS and      then re-parsing the CSS for var() statements and replacing them in browsers that don't natively support CSS variables.    </p>    <p>According to <a href="http://caniuse.com/#feat=css-variables">caniuse.com</a>, of current browsers only IE, Edge and Opera Mini do not support CSS variables. This polyfil appears to work on all three really well. I don't see why this wouldn't work on older browsers as well, but I haven't been able to test it on them yet.</p>
<p>As far as we can tell your browser <span class="supports">does <span class="no">not</span> support</span> native CSS variables. <span class="showforpolyfill">That means if you see green tests results below, it is thanks to the polyfill :).</span> <span class="hideforpolyfill">All the green test results below are actually native CSS Variable support. Good job using a good browser :)</span></p> <h3>Does this work on externally CSS files?</h3> <p>Yes!</p> <h3>Even ones loaded from another domain?</h3> <p>To go across domain, CSS needs to be served up with <code>Access-Control-Allow-Origin:*</code> headers.</p>

</div> <a href="#d" class="hide-docs">Toggle documentation</a> (for Opera Mini vs Codepen issue) <style> :root { --newcolor: #0f0; } .inlineoverlink { color: var(--success2); } </style> <h2>Tests</h2> <p>On mosts tests (unless otherwise noted) success will be green text. We start with a <code>color:red;</code> and then override it with a <code>color:var(--success);</code> (or similar) which is green.</p> <ul> <li><span class="samename">declare same variable over and over</span></li> <li><span class="demo1">no whitespace on var() calls</span></li> <li><span class="demo2">whitespace on var() calls</span></li> <li><span class="demo3">Multiple variables in same call. orange means first var worked, green var worked</span></li> <li><span class="inlineoverlink">orange if link won, green if style after link won</span></li> <li><span class="lower">--foo: lowercase foo</span></li> <li><span class="upper">--FOO: uppercase FOO</span></li> <li><span class="fallback">uses fallback <code>--var(--wrongname, green)</code></span></li> <li><span class="demo-import">css declared in an <code>@import</code></span> - not polyfilled yet. <a href="https://gist.github.com/stramel/91d05253f801f771da38b3bc7d3c765f#gistcomment-2258818">Identfied with a suggested fix</a>, but will require a bit of a re-write (to use document.styleSheets), so haven't done it yet.</li> </ul>
<h2>Tests on external, cross-domain file</h2> <div class="documentation"> <p><strong>Edge</strong> appears to be working well on Edge 13. Edge 12 was having some problems.</p> <p><strong>Opera mini</strong> seems to work well too. This demo fails because not all the page is displayed, but I think that is a codepen issue, not a polyfill issue. When the upper documentation is removed, all tests display well.</p> <p><strong>IE 11</strong> seems to do fine.</p> </div>
<ul> <li><span class="demo4">Gets stuff from external .css file. Should start red and change to green on LINK load. border proves the CSS loaded, missing colors means script didn't get parsed and reinserted</span></li> <li><span class="externalcolor">--externalcolor: should start red and change to green on LINK load</span></li> <li><span class="externalfallback">uses fallback. should be green</span></li> </ul>

<p>Another set of text under the test for Opera Mini testing.</p> <!-- Copy above for codepen update -->


Related Topics



Leave a reply



Submit