Styles are not rendered in the same order as what is specified in lit-html template
frankiefu opened this issue · 1 comments
Consider this:
import { LitElement, html } from '@polymer/lit-element';
import '@polymer/iron-icon/iron-icon.js'; // 3.0.0-pre.12
const SharedStyles = html`<style>button {background: blue;}</style>`;
class MyApp extends LitElement {
render() {
return html`
${SharedStyles}
<style>button {background: red;}</style>
<button>Should be red</button>
`;
}
}
If you run the above you will see the SharedStyles
style element is placed after the normal <style>
element so button has background: blue
instead of background: red
.
Note that if you comment out Polymer3's iron-icon.js
import then it works fine. Also this seems to start happening in 0.3.0
release which has ShadyCSS support.
cc @graynorton
This is a flaw with the way shady-render
is using ShadyCSS
to provide scoping.
When Shady DOM is in use
Shady CSS removes styles from element templates, scopes them, and puts them into the document.head. The shady-render
method in lit-html
calls ShadyCSS.prepareTemplate
once for each lit template in the element. The templates are individually processed outside-in, and any style elements are scoped per template and are placed in the head in reverse order (which seems wrong but fixing this wouldn't help). That means regardless of how the styles are intended to be ordered in the resultant DOM, they are ordered in reverse order of how the templates are processed by lit-html
. For example,
const styleA = `<style id="styleA">...</style>`
html`<style id="styleB">...</style>${styleA}`
Here styleA
is always incorrectly placed before styleB
regardless of where it is in the element DOM because it is in the 2nd template lit-html
processes for the element and ShadyCSS
puts these in reverse order.
When Shady CSS is loaded by native Shadow DOM is in use
ShadyCSS collapses all of the styles into one element that's placed at the top of the template. This breaks the case where a style is included as a part before a static style like this:
const styleA = `<style id="styleA">...</style>`
html`${styleA}<style id="styleB">...</style>`
When processed, styleB
is placed before the dynamic part for styleA
which is incorrect. This exact issue can be worked around by making styleB
a dynamic part, but this then interacts poorly with the ShadyCSS ordering so there's no good general workaround until this is addressed.