angular-material-css-vars icon indicating copy to clipboard operation
angular-material-css-vars copied to clipboard

IE11 Support

Open Coly010 opened this issue 5 years ago • 5 comments

Currently, as it stands, this library does not support IE11 as IE11 does not support CSS Variables.

A polyfill should fix this, and it may be more ideal to supply that polyfill with this library.

Coly010 avatar Jan 06 '20 17:01 Coly010

Hey there! Thanks for opening up this issue. I think the polyfill.ts offered by angular cli is a better place for that. What we should provide here however is explaining how to do it. I personally don't have any experience in making it work for IE11, but maybe somebody else could help?

johannesjo avatar Jan 06 '20 17:01 johannesjo

You are correct, it should be in polyfills.ts in the angular app, as you do not want to couple this lib with the polyfill. I will be doing some research into potential polyfills tomorrow so will update here when I have completed that.

Coly010 avatar Jan 06 '20 21:01 Coly010

@Coly010 Thank you! I would very much appreciate that!

johannesjo avatar Jan 07 '20 09:01 johannesjo

Ok so I got this to work 🎉

But it's ~~kind of~~ hacky.

We don't need a polyfill, rather a ponyfill. The ponyfill I used was css-vars-ponyfill. It's super quick and isn't too big bundle size: 586 kB. One of the biggest reasons for using it was that it allows declaration of global css props.

We also need to rewrite the _setStyle method within MaterialCssVarsService

The main reasoning behind this is due to IE11's restriction on the {element}.style property where {element} is a reference to any DOM Element. This restriction is that it will only allow CSS Style properties that it supports to be set as an inline style. Here's an example:

// HTML
<myElement>
</myElement>
// =========
// Set the style in JS
myElement.style = "--primary-color: blue; font-size: 18px;";

// Output in IE
// HTML
<myElement style="font-size: 18px">
</myElement>

As we can see, we've lost the --primary-color: blue', which means a lot of alternatives polyfills fail as they simply can't find the CSS Custom Property to polyfill it.

Therefore, I rewrote the _setStyle method to handle IE11 differently. Instead of setting the style attribute, I get it to set a data attribute. Then using NOTE: You must overwrite the singleton instance's _setStyle property!

const existingMethod = service['_setStyle'];

service['_setStyle'] = (vars: { name: string; val: string }[]) => {
    existingMethod(vars);

    const currentStyle = document.documentElement.getAttribute('data-mat-vars');

    const style = vars.reduce(
      (styleString: string, cssVar: { name: string; val: string }) => {
        styleString += `${cssVar.name}:${cssVar.val};`;
        return styleString;
      },
      currentStyle || ''
    );

    // Need to set a data attr for IE11 as it's style attr only allows for Style Attrs IE11 supports
    document.documentElement.setAttribute('data-mat-vars', style);
};

The next step is to initialise the ponyfill. This should ideally be done after we call our setPrimaryColor and setAccentColor. We want to read in our CSS Custom Props after they have been set by these methods as they will then exist in the DOM. We can read them in as a string and parse them into a key-value pair object the the css-vars-ponyfill accepts:

const documentElement = document.documentElement;

  if (documentElement.hasAttribute('data-mat-vars')) {
    const customThemePropsString = documentElement.getAttribute('data-mat-vars');
    const themeVariablesObj = {};

    // Convert from string of css vars into key-value pair
    customThemePropsString.split(';').forEach(prop => {
      const propParts = prop.split(':');
      themeVariablesObj[propParts[0]] = propParts[1];
    });

    // Let's init the ponyfill with our custom css variables
    cssVars({
      rootElement: document,
      variables: themeVariablesObj,
      watch: true
    });
  }

And voila!

The next step for improving this would be to have an Observable Subject in the service that when _setStyle is called we can run the code to re-gen the themeVariablesObj to continue to allow runtime switching of theme.

Coly010 avatar Jan 09 '20 20:01 Coly010

@Coly010 thank you very much for sharing!

johannesjo avatar Jan 09 '20 21:01 johannesjo

IE11 support has been deprecated in Angular v12 and removed in Angular v13, so this hack is obsolete now.

json-derulo avatar Apr 21 '23 13:04 json-derulo