From 15aee25a6b9755068d3c0d4c1c5b2653891400e8 Mon Sep 17 00:00:00 2001 From: Andy Luhrs Date: Fri, 6 Dec 2024 12:29:40 -0800 Subject: [PATCH 01/14] First start of atSheet explainer --- AtSheet/explainer.md | 146 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 AtSheet/explainer.md diff --git a/AtSheet/explainer.md b/AtSheet/explainer.md new file mode 100644 index 00000000..6e01ada1 --- /dev/null +++ b/AtSheet/explainer.md @@ -0,0 +1,146 @@ +# `@sheet` + +## Authors: + +- Andy Luhrs +- Kurt Catti-Schmidt +- TODO: Probably include Justin Fagnani? Tab Atkins? Dan and Tien? + +## Participate +- [Issue tracker](https://github.com/w3c/csswg-drafts/issues/5629) +- [Discussion forum](https://github.com/MicrosoftEdge/MSEdgeExplainers/labels/AtSheet) + +## Introduction +When developing web components, web authors often encounter challenges with distributing global styles into shadow roots and sharing styles across different shadow roots. Declarative shadow DOM (DSD) enables creation of shadow DOM without JS, but adding styles to DSD requires the developer to either use JS to put a shared stylesheet into `adoptedStyleSheets`, or to duplicate the styles in a ` +``` + +## Proposal - Adopting into Shadow DOM +Sheets can then be included in components via the `adoptedStylesheets` property via HTML: +```html + +``` +or imported from JS: +```html + +``` + +## Detailed design discussion + +### TODO: Performance? + +Dan's notes: +``` +Early thoughts: I like this, though it might not replace CSS bundlers in some really performance-sensitive cases. There is still one fetch incurred for the import {sheet1, sheet1} from './styles1and2.css' assert {type: 'css'}; statement that would be eliminated by bundling. If the perf benefit of eliminating this last extra fetch is greater than the perf benefit of parsing everything directly as CSS [1], then there might not be a performance win for using this instead of a bundler. + +But, it reduces the cost of using CSS modules in production to just 1 extra fetch, which is down from N extra fetches for N stylesheets. So for the cost of the one fetch, you cut out one part of the build/bundling process, get some perf benefit from parsing CSS directly without passing it through the JS parser, and the resulting production code will be easier to read and reason about than production code that had CSS bundled in the JS. + +[1] Last year I did some rough experiments to try to measure this potential perf benefit. I observed real differences in both time and memory, although you need a lot of iterations before it starts to be really observable: https://dandclark.github.io/json-css-module-notes/#css-module-performancememory-examples +``` + +### @import Data +```TODO +a more convenient way to write @import url("data:...");, with the potential to hook into CSS Modules a little better. +``` + +### Tab's questions +```TODO +I assume that the top-level import is still the overall stylesheet containing the @sheet rules, yeah? We just additionally specify that the @sheet rules produce additional exported sheets in the module object, keyed to their name? +The @sheet contents are independent, as if they were @import url("data:...");, right? The example in the preceding comment would indeed work (layer names are shared across all sheets already) but it wouldn't, say, see a @namespace rule in the outer sheet (and presumably could contain a @namespace rule of its own). +What's the behavior of @media (...) { @sheet {...} }? I presume the answer needs to be "it's invalid", and @sheet objects are required to be top-level. +Can you nest @sheets tho? If so, does the top-level import expose them all as a flat list, or do you just get the top-level ones, and have to go find the lower ones yourself? +If you have multiple @sheet foo rules, do we only take one and treat the rest as invalid (and then do we take first or last?)? Or do we merge their contents as if they were a single sheet? +``` + +```TODO +Yes. +Independent. While there could be something interesting about additional work around @import @sheet sheetName in order to share across the single file, the goal is to keep the various @sheet entries separate from each other, but bound to the single file download. +Invalid. @sheet should be top level. +Invalid. @sheet should be top level. +CSS rules say last definition wins. JS rules say multiple consts throw errors. CSS doesn't really throw errors, so keep towards the CSS rules here. +1 and 4 in concert beg the question of what is returned at import styles from './styles.css' assert { type: 'css' }; when the file does have @sheet specifiers? In the JS space, you'd hope for something more of an object with the other sheets on in { sheet1: CSSStyleSheet, sheet2: CSSStyleSheet, ...etc }, however, I'd expect we'd need to actually accept a CSSStyleSheet with its cssRules array including CSSStyleSheets, instead of rules. This could be a bit surprising to JS users, but clarifies the fact that 4 is not possible. If there were some magic way to get more of a JS import out of the the CSS assertion, it would be interesting to push for nested sheets to +``` + + +```TODO +since @sheet is meant to emulate an @import with a data url, should we let it take the same import restrictions - a MQ, a SQ, a layer? Or is it okay to expect these to be translated into rules inside the @sheet wrapping everything? +``` +### [Tricky design choice #1] + +[Talk through the tradeoffs in coming to the specific design point you want to make.] + +```js +// Illustrated with example code. +``` + +[This may be an open question, +in which case you should link to any active discussion threads.] + +### [Tricky design choice 2] + +[etc.] + +## Considered alternatives +==TODO== + +## Open Issues + +## References & acknowledgements + +[Your design will change and be informed by many people; acknowledge them in an ongoing way! It helps build community and, as we only get by through the contributions of many, is only fair.] + +[Unless you have a specific reason not to, these should be in alphabetical order.] + +Many thanks for valuable feedback and advice from: + +- [Person 1] +- [Person 2] +- [etc.] \ No newline at end of file From 6c1e94ff545264a71c045ac06abb7c24fcd48769 Mon Sep 17 00:00:00 2001 From: Kurt Catti-Schmidt Date: Wed, 18 Dec 2024 17:06:51 -0800 Subject: [PATCH 02/14] First round of additions --- AtSheet/explainer.md | 158 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 140 insertions(+), 18 deletions(-) diff --git a/AtSheet/explainer.md b/AtSheet/explainer.md index 6e01ada1..91440e20 100644 --- a/AtSheet/explainer.md +++ b/AtSheet/explainer.md @@ -4,31 +4,45 @@ - Andy Luhrs - Kurt Catti-Schmidt -- TODO: Probably include Justin Fagnani? Tab Atkins? Dan and Tien? +- TODO: Probably include Justin Fagnani? Tab Atkins? Dan and Tien? kschmi - added these names to the end. ## Participate - [Issue tracker](https://github.com/w3c/csswg-drafts/issues/5629) - [Discussion forum](https://github.com/MicrosoftEdge/MSEdgeExplainers/labels/AtSheet) +## Status of this Document + +This document is intended as a starting point for engaging the community and +standards bodies in developing collaborative solutions fit for standardization. +As the solutions to problems described in this document progress along the +standards-track, we will retain this document as an archive and use this section +to keep the community up-to-date with the most current standards venue and +content location of future work and discussions. + +* This document status: **Active** +* Expected venue: [CSS Working Group](https://www.w3.org/Style/CSS/) +* Current version: this document + ## Introduction When developing web components, web authors often encounter challenges with distributing global styles into shadow roots and sharing styles across different shadow roots. Declarative shadow DOM (DSD) enables creation of shadow DOM without JS, but adding styles to DSD requires the developer to either use JS to put a shared stylesheet into `adoptedStyleSheets`, or to duplicate the styles in a ` ``` -## Proposal - Adopting into Shadow DOM -Sheets can then be included in components via the `adoptedStylesheets` property via HTML: +This will import only this rules for "sheet1" - in this case, the rules for the `:host` selector, and will *not* import any rules from styles1and2.css outside of "sheet1". + +## Proposal - Importing a specific sheet via the `` tag +```html + +``` + +This will also import only this rules for "sheet1" - in this case, the rules for the `:host` selector, and will *not* import any rules from styles1and2.css outside of "sheet1". + +## Proposal - Importing a base set of inline styles into a Declarative Shadow DOM +Shadow DOM isolates styles, but fragment identifiers are global. This enables Declarative Shadow DOM to import `@sheet` references from the light DOM. + ```html - + +I'm in the light DOM +``` ## References & acknowledgements Many thanks for valuable feedback and advice from: From e3f8be4648d9920a4cb2448f770fc944b76a4970 Mon Sep 17 00:00:00 2001 From: Andy Luhrs Date: Thu, 9 Jan 2025 13:55:52 -0800 Subject: [PATCH 08/14] Minor acknowledgements and wording tweaks. --- AtSheet/explainer.md | 21 +++++++++------------ ShadowDOM/explainer.md | 2 ++ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/AtSheet/explainer.md b/AtSheet/explainer.md index 2d0f0f05..2917df52 100644 --- a/AtSheet/explainer.md +++ b/AtSheet/explainer.md @@ -5,6 +5,8 @@ - Andy Luhrs - Kurt Catti-Schmidt +Much of this explainer is consolidating and iterating on a CSSWG discussion around [Justin Fagnani](https://github.com/justinfagnani)'s proposal for multiple stylesheets in a single file [here](https://github.com/w3c/csswg-drafts/issues/5629). + ## Participate - [Issue tracker](https://github.com/w3c/csswg-drafts/issues/5629) - [Discussion forum](https://github.com/MicrosoftEdge/MSEdgeExplainers/labels/AtSheet) @@ -29,6 +31,8 @@ Additionally, bundling of stylesheets is difficult for developers who are distri We propose an enhancement to allow declaration of new stylesheets via an `@sheet` CSS block, and using existing mechanisims such as `@import` and `` to apply those shared styles to DSDs without the use of Javascript. +We're currently investigating this and [Declarative CSS modules](/ShadowDOM/explainer.md) in parallel, and anticipate that we'll be prioritizing only one of these two in the immediate future. + ## Goals * Allow the reuse of styles in markup-based shadow DOM without requiring JavaScript. * Allow reuse of styles in markup-based shadow DOM without requiring external network requests. @@ -76,8 +80,7 @@ This will import only this rules for "sheet1" - in this case, the rules for the This will also import only this rules for "sheet1" - in this case, the rules for the `:host` selector, and will *not* import any rules from styles1and2.css outside of "sheet1". ## Proposal - Importing a base set of inline styles into a Declarative Shadow DOM -Shadow DOM isolates styles, but fragment identifiers are global. This enables Declarative Shadow DOM to import `@sheet` references from the light DOM, and vice versa. -**TODO: point to frament-only identifiers docs** +Shadow DOM isolates styles, but fragment identifiers are global. This enables Declarative Shadow DOM to import `@sheet` references from the light DOM. ```html ``` -This will import only this rules for "sheet1" - in this case, the rules for the `:host` selector, and will *not* import any rules from styles1and2.css outside of "sheet1". +This will import only this rules for `foo` - in this case, the rules for the `:host` selector, and will *not* import any rules from `sheet.css` outside of "foo". ## Proposal - Importing a specific sheet via the `` tag ```html - + ``` -This will also import only this rules for "sheet1" - in this case, the rules for the `:host` selector, and will *not* import any rules from styles1and2.css outside of "sheet1". +This will also import only this rules for "foo" - in this case, the rules for the `:host` selector, and will *not* import any rules from `sheet.css` outside of "foo". ## Proposal - Importing a base set of inline styles into a Declarative Shadow DOM Shadow DOM isolates styles, but fragment identifiers are global. This enables Declarative Shadow DOM to import `@sheet` references from the light DOM. ```html ``` or imported from JS: ```html ``` @@ -108,28 +110,20 @@ or imported from JS: #### Named Imports with Imperative Shadow DOM -The following examples use a stylesheet saved as `sheet.css` with the following contents: - -```css -div { color: blue; } - -@sheet bar { div { color: red; } } -``` - -This can then be imported via Javascript as follows: +`sheet.js` can also be imported via Javascript as follows: ```js -import foo, { bar } from 'sheet.css' with { type: 'css' } +import baz, { bar } from 'sheet.css' with { type: 'css' } ``` -`foo` will reference style rules outside of any `@sheet` blocks as a Default Import (in this case, the `div { color: blue; } ` rule). +`baz` will reference style rules outside of any `@sheet` blocks as a Default Import (in this case, the `div { color: blue; } ` rule). `bar` will reference style rules within the `@sheet bar` block as a Named Import (in this case, the `div { color: red; } ` rule). Named imports may be renamed as part of this import process: ```js -import foo, { bar as baz } from 'sheet.css' with { type: 'css' } +import baz, { bar as renamed } from 'sheet.css' with { type: 'css' } ``` The default import may be omitted, importing only the named `@sheet`: @@ -148,14 +142,7 @@ shadowRoot.adoptedStyleSheets = [bar]; #### Performance -This will be a performance-neutral feature, and use of it may allow for developers to reduce the number of network requests. We should ensure that multiple imports of different sheets from the same file produce a single network request. The following examples use a stylesheet saved as `sheet.css` with the following contents: - -```css -div { color: blue; } - -@sheet foo { div { color: red; } } -@sheet bar { div { font-family: sans-serif; } } -``` +This will be a performance-neutral feature, and use of it may allow for developers to reduce the number of network requests. We should ensure that multiple imports of different sheets from the same file produce a single network request. ```js // The following two imports should only make a single network request. From eeec1328de84dbec76955aca34c31f1e3c462be5 Mon Sep 17 00:00:00 2001 From: Andy Luhrs Date: Thu, 9 Jan 2025 14:11:23 -0800 Subject: [PATCH 10/14] Missed an example --- AtSheet/explainer.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/AtSheet/explainer.md b/AtSheet/explainer.md index 150fdec4..226e6c82 100644 --- a/AtSheet/explainer.md +++ b/AtSheet/explainer.md @@ -201,17 +201,17 @@ interface CSSStyleSheet : StyleSheet { ```html - + I'm in the light DOM ``` ## References & acknowledgements @@ -222,4 +222,4 @@ Many thanks for valuable feedback and advice from: - Justin Fagnani - Tab Atkins Jr. - Tien Mai -- Westbrook Johnson \ No newline at end of file +- Westbrook Johnson From 0f7311ded107ee08da3f144d318b0123d93d97f2 Mon Sep 17 00:00:00 2001 From: Andy Luhrs Date: Thu, 9 Jan 2025 14:12:11 -0800 Subject: [PATCH 11/14] Missed one more... --- AtSheet/explainer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AtSheet/explainer.md b/AtSheet/explainer.md index 226e6c82..9d9c2f86 100644 --- a/AtSheet/explainer.md +++ b/AtSheet/explainer.md @@ -76,7 +76,7 @@ This will import only this rules for `foo` - in this case, the rules for the `:h ## Proposal - Importing a specific sheet via the `` tag ```html - + ``` This will also import only this rules for "foo" - in this case, the rules for the `:host` selector, and will *not* import any rules from `sheet.css` outside of "foo". From 977a53ecf90f9ed1092aca143b2e86456aae5dc4 Mon Sep 17 00:00:00 2001 From: Andy Luhrs Date: Thu, 9 Jan 2025 14:20:03 -0800 Subject: [PATCH 12/14] Add to readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 71081dac..c301624f 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ we move them into the [Alumni section](#alumni-) below. | [Handwriting attribute](Handwriting/explainer.md) | ![GitHub issues by-label](https://img.shields.io/github/issues/MicrosoftEdge/MSEdgeExplainers/Handwriting?label=issues) | [New issue...](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/new?assignees=adettenb&labels=Handwriting&template=Handwriting.md&title=%5BHandwriting%5D+Issue) | HTML | | [AudioContext Interrupted State](https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/AudioContextInterruptedState/explainer.md) | ![GitHub issues by-label](https://img.shields.io/github/issues/MicrosoftEdge/MSEdgeExplainers/AudioContext%20Interrupted%20State?label=issues) | [New Issue...](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/new?assignees=gabrielbrito&labels=AudioContext+Interrupted+State&title=%5BAudioContext+Interrupted+State%5D+%3CTITLE+HERE%3E) | WebAudio | | [IndexedDB getAllRecords()](https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/IndexedDbGetAllEntries/explainer.md) | ![GitHub issues by-label](https://img.shields.io/github/issues/MicrosoftEdge/MSEdgeExplainers/IndexedDB%20GetAllRecords?label=issues) | [New Issue...](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/new?assignees=SteveBeckerMSFT&labels=IndexedDB%20GetAllRecords&title=%5BIndexedDB+getAllRecords()%5D+%3CTITLE+HERE%3E) | IndexedDB | - +| [Mulitple Stylesheets Per File (@sheet)](https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/AtSheet/explainer.md) | | [New Issue...](https://github.com/w3c/csswg-drafts/issues/5629) | CSS | # Alumni 🎓 From d65d1b9a54a534ff6676c4f98681648dd4a90903 Mon Sep 17 00:00:00 2001 From: Andy Luhrs Date: Thu, 9 Jan 2025 14:50:30 -0800 Subject: [PATCH 13/14] Fix old example --- AtSheet/explainer.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AtSheet/explainer.md b/AtSheet/explainer.md index 9d9c2f86..39e99047 100644 --- a/AtSheet/explainer.md +++ b/AtSheet/explainer.md @@ -72,14 +72,14 @@ div { ``` -This will import only this rules for `foo` - in this case, the rules for the `:host` selector, and will *not* import any rules from `sheet.css` outside of "foo". +This will import only this rules for `foo` - in this case, the `div { color: red; }` rule, and will *not* import any rules from `sheet.css` outside of "foo". ## Proposal - Importing a specific sheet via the `` tag ```html ``` -This will also import only this rules for "foo" - in this case, the rules for the `:host` selector, and will *not* import any rules from `sheet.css` outside of "foo". +This will also import only this rules for "foo" - in this case, the `div { color: red; }` rule, and will *not* import any rules from `sheet.css` outside of "foo". ## Proposal - Importing a base set of inline styles into a Declarative Shadow DOM Shadow DOM isolates styles, but fragment identifiers are global. This enables Declarative Shadow DOM to import `@sheet` references from the light DOM. From 6a3bdf6d5342e1166b4ec756e6cb70b379f934c7 Mon Sep 17 00:00:00 2001 From: Andy Luhrs Date: Thu, 9 Jan 2025 16:12:37 -0800 Subject: [PATCH 14/14] Update AtSheet/explainer.md Co-authored-by: Dan Clark --- AtSheet/explainer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AtSheet/explainer.md b/AtSheet/explainer.md index 39e99047..18f62f09 100644 --- a/AtSheet/explainer.md +++ b/AtSheet/explainer.md @@ -29,7 +29,7 @@ When developing web components, web authors often encounter challenges with dist Additionally, bundling of stylesheets is difficult for developers who are distributing web components. They either need to ship many small stylesheets, or use workarounds like `@import url("data...")` which are suboptimal for performance and don't interact well with other patterns. -We propose an enhancement to allow declaration of new stylesheets via an `@sheet` CSS block, and using existing mechanisims such as `@import` and `` to apply those shared styles to DSDs without the use of Javascript. +We propose an enhancement to allow declaration of new stylesheets via an `@sheet` CSS block, and using existing mechanisims such as `@import`, ``, and CSS module script `import` to apply those shared styles to DSDs without the use of Javascript. We're currently investigating this and [Declarative CSS modules](/ShadowDOM/explainer.md) in parallel, and anticipate that we'll be prioritizing only one of these two in the immediate future.