angular-material-css-vars
angular-material-css-vars copied to clipboard
IE11 Support
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.
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?
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 Thank you! I would very much appreciate that!
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 thank you very much for sharing!
IE11 support has been deprecated in Angular v12 and removed in Angular v13, so this hack is obsolete now.