How do you scope Polymer @imports/stylesheets which are dynamically injected into an element?
-
23-12-2019 - |
Question
I'm currently working on a Polymer element for Prism.js (a syntax highlighter). The element is hosted here and is also in a GitHub repo. What I'm trying to do is allow a user to set an attribute theme
which decides which theme file we pull in. A user should be able to set a different theme per element with those styles being correctly scoped.
For example, if you configure the element with <prism-js theme="tomorrow">
, when the element initializes we read in the theme
value and pull in prism-tomorrow.css
by injecting it via an @import
. If I have another instance <prism-js theme="coy">
, the themes pulled in for each element shouldn't clash.
Unfortunately, although the output for this is correct in Canary (the styles for each element don't clash)..
The styles aren't scoped and clobber each other in Chrome stable:
I'm wondering if there's a reliable way to approach this problem.
To date I have attempted to:
- Inject my styles with the
scoped
attribute - this either isn't supported or didn't work - Inject a link to the stylesheet which is appended to my
shadowRoot
- this had the same problem of styles clashing - Use
shimStyling
- same issue - Work against my template's stylesheet as per the Polymer docs:
var sheet = document.querySelector('style').sheet;
var hostRules = sheet.cssRules[0];
hostRules.cssText = '@import url("' + this.themepath + '");</style>';
Alternatively, I'm wondering if it is possible set up my element so that I can update {{theme}}
in my template's @import
as follows:
<style>
:host { display: block; };
@import 'bower_components/prismjs/prism-{{theme}}.css';
</style>
Though, I'm unsure if getting that to work would solve my scoping issues either. Unfortunately inlining all of the themes is not an option. Any ideas? :)
Solution
Sadly, it may not be practical to achieve per-instance theme selection under the ShadowDOM polyfill as it is today.
The CSS scoping available to native ShadowRoot is quite difficult to polyfill. Today the polyfill does lots of tricks and translations in order to provide some scoping while still remaining efficient.
However, there are two categories of scoping issues that are problematic:
- per-instance scoping (today the CSS shim operates per-type)
- lower-bound encapsulation (an element's styles can leak into other elements in it's subtree)
There are still a lot of things that work well given these constraints, but, in particular, consuming CSS like you want to do is not well supported.
Work is under way to explore imperative support for (simulated) per-instance scoping via the CSS shim. I will endeavor to update this issue if something lands.