W3C

– DRAFT –
Sharing styles with Declarative Shadow DOM

25 September 2024

Attendees

Present
alisonmaher, bkardell_, dandclark, ethanjv, JRJurman, jyasskin, kbabbitt, keithamus, kizu, masonf, sanketj
Regrets
-
Chair
Kurt Catti-Schmidt
Scribe
dandclark

Meeting minutes

Slideset: https://docs.google.com/presentation/d/1GfFzROUKWqUde300qDDzi9ydKdpjuOVX9L7kGLyZcpA/edit?usp=sharing (archived PDF copy)

<alisonmaher> explainer: https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/ShadowDOM/explainer.md

kurt: <presents slides linked above>
… : Style sharing in declarative shadow dom is unsolved problem
… : adoptedStyleSheets allows you to have styles that don't apply to root doc, and shadow roots can opt in
… : Can plug in mutliple sheets
… : Shouldn't have to compromise with markup-based version
… : DSD doesn't have this yet.
… : DSD has advantages. Perf, developer friendly.
… : No JS, SEO friendly
… : You can use JS adoptedStylesheets with DSD, but that requires script. Can have FOUC. Ergonomics are worse.
… : Doesn't work with script disabled
… : Using <link rel> in <template> is a workaround solving lots of these problems. Why do we still need another solution?
… : Because we don't always want to use an external file. Sometimes prefer inline styles.
… : Parsing and applying other stylesheet can come after FCP
… : DataURI needs to be parsed separately for each instance. Dev ergonomics not good.
… : Only one stylesheet per link tag. Not good if your styles are modular; the issues get multipled per sheet.
… : Ideal solution is all markup-based, supports inline styles, aligns with script-based adoptedStyleSheets, can define style without applying them.
… : Should be forward-looking towards other resource types.
… : One suggestions is for fully-open styles for shadow DOM. This solves problem but still leaves gap with adoptedStyleSheets. Confusing to have isolation with script but not style. Styles can leak.

Justin: (In other chat?) These solve a different problem.

kurt: Currently not a notion of defining a stylesheet in markup and not applying it to main document.
… : Imperative adoptedStylesheets must be constructed. This requirement has been loosened in spec but not implemented yet.
… : Proposal @layer and adoptStyles, supported in Chromium

(Many people): Everyone supports

kurt: Can selectively inherit styles, rename them
… : Doesn't quite solve issue because doesn't integrate well with adoptedStyleSheets. Doesn't have ability to define styles without apply to light DOM by default. Not extensible for other resource types. I did prototype this but not sure it's right solution.
… : Other proposal for @sheets. Give identifiers to sheets, give it to adoptedStylesheets on template. Still can't define styles without applying to light DOM. Not extensible to other resource types.

ntim: It's not standardized.

bkardell: What do you mean it's not extensible?

kurt: Devs might want to share other types of resources. E.g. Images

dandclark: Particularly HTML

masonf: One of your requirements was not passing styles through multiple layers?

kurt: Yes, this would have that issue.

<michael_warren> for posterity from zoom:

<michael_warren> imo the question of whether or not @sheet is the right approach is a big one for any sort of “add styles to shadow roots” approach

<justinf> @sheet doesn't work

OwenBuckley: To make sure I understand, you're looking for hot-linking but in different medium?

kurt: Want it to be pure markup

justinf: Pointing out some options. Key use case is SSR. Often or all the time with SSR don't know ahead of time what components you will emit. So declaring styles above and then using them later is not enough. Need to be able to emit style deep in a component and have it be usable later.

kurt: ID-based solutions don't work because it's not global

<justinf> there's an existing problem with JS modules of sharing inline modules with imported modules. Maybe you want to inline a polyfill and import it later when loading the main JS bundle, without duplication

<justinf> so a specifier-based solution could apply to _all_ modules, regardless of type

kurt: CSS module scripts can be used with adoptedStyleSheets. Extension to script-based adoptedStyleSheets.
… : It's elegant, CSS module syntax is powerful
… : Can imagine declarative version of this.
… : Define CSS module in <script> tag. Put the specifier in <template adoptedstylesheets> attribute to apply it to shadow.

<obuckley> wouldn't you lose syntax highlighting? how come not a style tag but with similar API?

kurt: : This is the solution we want to look into. Meets the requirements we listed.

<justinf> why would you need syntax highlighting?

<jyasskin> This doesn't immediately meet Justin's requirement that the server-side renderer can discover its shared stylesheets as it runs, right?

<obuckley> isn't syntax highlighting nice? not a deal breaker, but most folks seem to value it in their IDE i guess

kurt: : Is markup-based, scoping is global for the document

<justinf> you would most likely be writing your CSS in .css files

<justinf> this is for SSR

<michael_warren> syntax highlighters could probably be updated to <script type="css-module"> as css instead of js...knowing the little i know of codemirror language parsers I think thats not terrible hard?

kurt: : There's another session today

masonf: It's in the module graph?

kurt: Yes

<justinf> no one is going to write in this format

<justinf> just like people don't write SSR output by hand

<jyasskin> Even if they do, editors can realize that `type=css-module` should be syntax-highlighted.

<justinf> sure

kurt: We think this CSS-modules based solution is the best. What do others think?

jyasskin: Justin mentioned SSR requrement. Script that's rendering discovers it needs a sheet, can use it later. Not sure if declarative CSS modules meet that.

<obuckley> ok, i thought this was a <style> defined elsewhere like in a HTML page, and this was "hot linking" that to the template. so wouldn't the CSS likely be in something ending in .html?

kurt: I agree

<obuckley> i guess i was just curious why not use a style tag + the custom attribute is all

<justinf> because what it's really doing is defining a module script in the module graph

<obuckley> but I guess it's a CSS "module" so more on the JS side

<miriam> and a style element would apply the styles to the page by default

<obuckley> got it, that makes sense

<obuckley> thanks

dandclark: This should work for SSR case

<justinf> <script> is more general

<justinf> you could use the for JSON too

<michael_warren> <style type="module">?

<justinf> then what do you do for JSON?

<Romain> +1 for using <template>

<justinf> The <script> solution neatly solves many other problems too

<michael_warren> yeah, that does feel a bit piecemeal whereas a more standardized specifier solution for modules of different kinds that current exist but is also extensible for new module types later on

<masonf> dandclark: let's decide if this is a good idea first, then we can bikeshed

justinf: Editors can update to do syntax highlighting for this

<justinf> whatwg/html#7367

justinf: I like this solution. There's lots of problems relating to modules this can solve. Want to import script early on, then use it later on when stuff loads. Set up your script then use it later from your app bundle.

<obuckley> yeah, if it becomes a standard then yeah in theory all IDEs should adopt. in the meantime, will keep waiting for nesting highlighting in VSCode :)

justinf: : Longstanding issue with module-related properties in script tag. Solves that as well. Want to get access to exports of a given script tag, can just import it.
… : Works with JSON, other module types that come in the future. This version is really powerful. Has a lot of open questions about module graph and timing though. Race conditions like you load easily and do SSR, have 2 competing things. One tries to define specifier and one loads it. One hits the network and one doesn't. What happens, do you abort fetch?

kurt: Yes we need to think about these

justinf: Neeed to think about how this works with import maps
… : Also includes integrity. Can think about these inline scripts telling browser something about module and also populating module map
… : Can say this pattern for these types of paths are going to be inline.

westbrook: This all looks great. Let's investigate more deeply. You're already open to investigating other syntaxes. Use of script tag causes problems if JS is turned off. Talk about putting this into module graph.

<kbabbitt> dandclark: in chromium, the point where we do those checks is when parsing a script tag, I can't think of a reason where you can't run that stuff with script turned off...

kurt: Seems workable, not sure what haappens today. Good question.

<justinf> there's a similar question + answer for HTML modules

kurt: : May or may not be issue, but it should be solvable

<justinf> <style> is the default solution for Lit SSR now

emilio: I was surprised to not see mention of inline styles. If this is a thing for SSR that's the obvious thing. Causes duplication in DOM but not style engine. Is that intentional omission?

<justinf> it bloats the payload hugely

<justinf> gzip helps a lot, but there's still parsing time

<justinf> ah, thanks

kurt: I should have listed that. The engines will optimize.

masonf: Doesn't play well with adoptedStylesheets, they're copy on write

emilio: Generally the CSSWG has been against mutating CSSOM stuff. But yes, the semantics may not be what you want

justinf: You have to duplicate styles in your payload

<kbabbitt> dandclark: that's similar to feedback we've gotten from developers internally ,want to avoid duplication of DOM & stylesheets

emilio: Don't you have the declarative duplication in the DOM?

justinf: You want to be able to remove that

<jyasskin> dandclark: And this proposal could generalize to deduplicating HTML structure.

<jyasskin> bkardell_: That's an argument to use <template>.

<justinf> <script ype="html">

<jyasskin> ryosuke: <Script> won't run either, but it can't be nested.

masonf: I am sympathetic to emilio's point. But in the DOM repeatd compoents are repeated, that's not necessarily the case for CSS

emilio: Not sure I get that.

masonf: Issue is people think why is it repeated, it shouldn't have to be
… : If you have 3 components there are 3 in dom by definiiton

emilio: Before, people would innerHTML the whole tree. Not trying to say stop, just curious. Seemed like an omission

kurt: Yes we should have included this

keithamus: This doesn't include images?

kurt: Not yet
… : But this could set up for that

keithamus: I'm concerned about timing of when these get declared.
… : Potential new vector authors must consider for 3p components. They define a module, then their identifiers clobber mine. Could be incidental, could be malicious. 3p modules defines a bunch of these, with common names. And how does that work in terms of timing? I define, someone else defines, what happens?

kurt: Yes we need to explore this.

keithamus: Need to think through story of HTML module. Given that closing script tag is big footgun. Maybe <template> be better. Can you have nested templates?

rniwa: yes

<justinf> we should consider WASM modules too. Do people want to inline a WASM module, then import it into an inline JS module to kick it off?

keithamus: I can see you wanting to have script tag in HTML module. Not allowed in script, but <template> allows matching

rniwa: How does it work with prefetch?

<justinf> ah

<Zakim> specifiers, you wanted to react to michael_warren

<justinf> argh, I'm terrible at this

michael_warren: I know this is baked into sharing with DSD. Can extend to JS- based components? Also, clarifying q about specifiers. They look like file paths but do they need to be? What if you identifier clobbers actual file?

<Zakim> justinf, you wanted to react to keithamus and to talk about specifiers

<masonf> ack +

<Zakim> +, you wanted to talk about specifiers, collisions

<jyasskin> Dynamic import maps (whatwg/html#10528) look related to the way this proposal extends the module map.

justinf: This is the JS version. It is a DOM version of what exists. Not everyone uses existing version because not supported in all browsers. We would synthesize specifier, use that as a global ID until people shift to using this. Problem with imperative version is you have object sitting in JS heap, how do I serialize that to the DOM? I proposed global ID. Can treat specifiers like that.

kurt: that was xid?

justinf: yes

<michael_warren> would this solution also deal with styles/modules added dynamically? particularly in the imperative case?

rniwa: Earlier point about prefetch -- if we do this in declarative, there will be delay between when sheet appears and browser fetches subresources.

<michael_warren> i think thats one of the problems with the imperative approach right now...what happens when new sheets are added dynamically like light.css default and dark.css added once the user clicks the mode switch button etc

rniwa: : Need to solve how we'll get subresources in time

<justinf> the sub-resource problem isn't any worse than the existing solutions, yeah?

<justinf> <style>...</style> already has this?

<masonf> dandclark: on the prefetch thing, you can do module preload for external resources. You can inline the rest. We should talk more.

<masonf> dandclark: third party resource overwrite problem: you already have to worry about that with existing modules. When I import a third party module, they can put whatever they want into the graph anyway. Maybe this is not a new problem?

<westbrook> How can we support on going research?

<justinf> @import should be banned

<justinf> it's banned in constructible stylesheets

<masonf> dandclark: about @import, not sure about this. We haven't solved this anyway, so we need to figure it out.

<masonf> dandclark: we do the same thing we do today, which is failing the import. Or whatever we do.

kurt: Please attend CSS modules session later today. Hopefully we agree this is the solution to pursue. Please raise issues.

Minutes manually created (not a transcript), formatted by scribe.perl version 235 (Thu Sep 26 22:53:03 2024 UTC).