Open bicknellr opened 5 years ago
They have to be put somewhere, and putting them before all the stylesheets coming from link
/etc seems odder than putting them after.
Sounds like this is a subjective issue, so here's my preferred color for the bike shed:
I conceptually think of the style sheets in a single root as if they were concatenated into a single style sheet since they pretty much work this way. Adding a style sheet to adoptedStyleSheets
seems akin to adding @import
statements to the concatenated style sheet. When importing a style sheet with @import
, I'd usually put it at the top so that my inline rulesets will override anything that I'm getting from the import.
So, if we use this example:
const sheet1 = new CSSStyleSheet();
sheet1.replaceSync(".foo { color: red; }");
const div = document.createElement("div");
const shadowRoot = div.attachShadow({ mode: "open" });
shadowRoot.adoptedStyleSheets = [sheet1];
shadowRoot.innerHTML = `
<style>.foo { color: blue; }</style>
<span class="foo">Hello!</span>
`;
It feels more like it should be:
@import 'data:text/css,.foo { color: red; }';
.foo { color: blue; }
than:
.foo { color: blue; }
@import 'data:text/css,.foo { color: red; }';
I think this sentiment becomes even more applicable if CSS Modules become a thing. But I totally recognize that it's not critical since I could just stop adding inline <style>
elements and switch everything over to using adoptedStyleSheets
to get the same effect.
I see where you're coming from within shadow roots, but consider the issue on the outer page, where .styleSheets
will usually only include stylesheets coming from <link>
in the <head>
. It seems a lot weirder (to me, at least) for the JS-created adopted stylesheet to default to being earlier in the cascade than the <link rel=stylesheet>
.
If we could somehow put it in the middle, where everything in the head
came first, then adopted stylesheets, then style
in body
, I think that would be the best solution. But all markup-based stylesheets are bodged together into .styleSheets
, so we instead have to choose to put the adopted sheets before or after all of them. :/
Putting JS-managed stuff after HTML-managed stuff is the general pattern for this sort of thing, so that's what we went with.
(Really, what we want is the ability to tag the adopted stylesheet as part of a specific Cascade Origin, so you could set things up as being "user-agent" level, and thus automatically overridden by anything at author-level, such as style
contents. But that's not currently possible; perhaps in a v2?)
What if adoptedStyleSheets
was moved from DocumentOrShadowRoot
to HTMLStyleElement
? Instead of attaching style sheet objects to a single bucket that applies to the entire root and requires making the 'before vs. after' decision, you would attach them to a specific node instead and inherently be able to decide the placement yourself.
Main document:
const externalStyles = document.createElement('style');
externalStyles.adoptedStyleSheets = [a, b, c];
// If you want adopted styles after everything else you've imported:
document.head.appendChild(externalStyles);
In a ShadowRoot:
const div = document.createElement("div");
const shadowRoot = div.attachShadow({ mode: "open" });
shadowRoot.innerHTML = `
<style>.foo { color: blue; }</style>
<span class="foo">Hello!</span>
`;
const externalStyles = document.createElement('style');
externalStyles.adoptedStyleSheets = [a, b, c];
// If you want adopted styles before everything in the root:
shadowRoot.insertBefore(externalStyles, shadowRoot.firstChild);
You could put them in multiple places if that's what you needed:
<style id="alwaysLoses"></style>
<style>.foo { color: red; }</style>
<style id="alwaysWins"></style>
<script>
document.getElementById('alwaysLoses').adoptedStyleSheets = [a, b];
document.getElementById('alwaysWins').adoptedStyleSheets = [c, d];
</script>
edit: Removed an incorrect line in the second example.
If you're going to give them before/after locations in the DOM tree, they should just be DOM nodes. For example, HTMLStyleElement.
If you're going to give them before/after locations in the DOM tree, they should just be DOM nodes. For example, HTMLStyleElement.
CSSStyleSheet could have a method that produces a new HTMLStyleElement associated with the style sheet. Using createLinkedStyleElement
as the name, then this example (that I updated to use prepend
)
const div = document.createElement("div");
const shadowRoot = div.attachShadow({ mode: "open" });
shadowRoot.innerHTML = `
<style>.foo { color: blue; }</style>
<span class="foo">Hello!</span>
`;
const externalStyles = document.createElement('style');
externalStyles.adoptedStyleSheets = [a, b, c];
shadowRoot.prepend(externalStyles);
would become
const div = document.createElement("div");
const shadowRoot = div.attachShadow({ mode: "open" });
shadowRoot.adoptedStyleSheets = [sheet1];
shadowRoot.innerHTML = `
<style>.foo { color: blue; }</style>
<span class="foo">Hello!</span>
`;
shadowRoot.prepend(...[a, b, c].map(sheet => sheet.createLinkedStyleElement()));
Is that kind of what you were thinking?
Yes, although I'm not sure there's too much value in indirecting through CSSStyleSheet instead of just creating a HTMLStyleElement directly.
I was thinking that the node created by createLinkedStyleSheet
would continue to be associated with the CSSStyleSheet rather than containing a copy of the style sheet at the time they were created - replace
, etc. would 'update' all of the associated nodes.
I was thinking even simpler: just use HTMLStyleElement only, don't ever use a CSSStyleSheet apart from htmlEl.sheet.
I'm not sure I follow - could you give an example?
document.adoptedStylesheet
will be used most of the time by web component authors to provide the default styles of components, leaving the users the ability to customizable and override the styles using link or inlined <style>
. This is pretty much broken, when they are applied after.
In fact, I thought it behaved like user agent's stylesheets.
While this problem is more important in the document
case, it's too for shadow-dom, where developers should be able to add a
From https://wicg.github.io/construct-stylesheets/#using-constructed-stylesheets:
Is there a particular reason that style sheets in
adoptedStyleSheets
are ordered after style sheets from<style>
s in the associated tree? AFAICT,adoptedStyleSheets
is being designed primarily as a mechanism for sharing style sheets amongst many elements, but it feels strange that shared styles would take precedence over styles that are certainly only applicable to a particular instance (i.e.<style>
in the associated tree).p.s. Yes, you could just add your instance-specific style sheets to the end of the instance's
adoptedStyleSheets
instead of inserting them into the tree, but why is that necessary?