[ShadyCSS] Constructible Stylesheets and adoptedStyleSheets support for browsers with native shadow DOM
justinfagnani opened this issue · 4 comments
Constructible Stylesheets
We want to support and API as close as possible to native Constructible Stylesheets, so that this works:
const s = new CSSStyleSheet();
s.replaceSync(':host { display: block } ');
This may be achievable by overwriting the CSSStyleSheet constructor with one that creates a stylesheet in a shadow root. Something like:
const container = document.createElement('constructible-stylesheets');
container.attachShadow({mode: 'open'});
document.append(container);
const originalCSSStyleSheet = CSSStyleSheet;
CSSStyleSheet = class extends originalCSSStyleSheet {
constructor() {
this._styleElement = document.createElement('style');
container.shadowRoot.append(this._styleElement);
Object.setPrototypeOf(this, this._styleElement.sheet);
}
replaceSync(styleText) {
this._styleElement.data = styleText;
}
}
(I haven't tried this code)
adoptedStyleSheets
ShadowRoot.adoptedStyleSheets
could be implemented with a setter that only accepts arrays of stylesheets created as above with a _styleElement
property. It would then clone those style elements and insert them last into the shadow root.
To protect against mutations of the shadow root that remove the styles, we can put them in a custom element called <adopted-stylesheets>
that adds itself back to the shadow root if it's ever removed.
const allAdoptedStylesheets = new Set();
class AdoptedStylesheetsElement extends HTMLElement {
constructor() {
super();
allAdoptedStylesheets.add(this);
}
connectedCallback() {
this._previousRoot = this.getRootNode();
}
disconnectedCallback() {
this._previousRoot.append(this);
}
}
allAdoptedStylesheets
can be used to update cloned style tags incase replace
or replaceSync
is called on a CSSStyleSheet.
cc @azakus
While implementing this enhancement, please mind the CSP (Content-Security-Policy). Some applications may have a very strict policy (e.g. forbid inline anonymous <style>
elements), which may cause a lot of problems. One of possible ways of working around this issue would be to provide a possibility of using a nonce
attribute on <style>
element:
CSSStyleSheet = class extends originalCSSStyleSheet {
(...)
fixCsp(nonce) {
this._styleElement.nonce = nonce;
}
}
This would result in attaching <style nonce="...">
element to (shimmed?) shadow DOM, and this could satisfy CSP's needs. (Of course that nonce
value must be provided by code that uses ShadyCSS.)
@justinfagnani, the construct-styles-sheets-polyfill project may be interesting for you. It supports the adoptedStyleSheets
functionality in the following ways:
- For all the browsers with native support for web components, it supports document-level and shadow root-level style sheets adoption.
- For browsers without support for web components (IE 11 and stable Edge for now), it implements only the document-level style sheet adoption.
It can also be used along with the ShadyCSS's prepareAdoptedCssText
because their functionalities do not overlap.
const originalCSSStyleSheet = CSSStyleSheet; CSSStyleSheet = class extends originalCSSStyleSheet { constructor() { //... } }
CSSStyleSheet is not instantiable, so it's not extensible either ...
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.