Re: [csswg-drafts] Proposal: CSS @sheet (#11509)

> Good proposal 👍 Thank you for raising this!

Glad you like it! 👍 I appreciate your thoughts and suggestions and took some time going over them.

> 
> `@sheet` represents an inlined `.css` file, right? So shouldn't this match `@import` in as many ways as possible? They should be equivalent and there shouldn't be syntactic benefits or drawbacks to either.

You're correct that `@sheet` has a lot of similarities with `@import`, in particular that they both bring in style rules from a separate file, but the real power of `@sheet` is that it binds a set of rules to an identifier that can be imported in contexts beyond CSS.  This identifier is really important, as it allows `@sheet` blocks to be imported in different contexts (e.g. `<link rel>`, `@import`, and CSS Modules). Conceptually, you can think of each `@sheet` block as a separate CSS file that gets represented by its `@sheet` identifier instead of a file name.

> 
> With that in mind I think that nesting should be allowed and that using `@import` in `@sheet` should also be allowed.
> 
> These should all be equivalent:
> 
> 1:
> 
> `index.css`
> 
> @sheet foo {
>   @sheet bar {}
> }

If this were to be referenced by a `<link rel>` tag, how would you target `bar`? We could differentiate nested sheets with some sort of delimiter (e.g. if we used `#` to delimit between nested sheets this could be valid - `<link rel="stylesheet" href="index.css" sheet="foo#bar">`), but I am struggling to come up with a use case. What types of situations would nested `@sheet` definitions enable?

> 2:
> 
> `index.css`
> 
> @sheet foo {
>   @import "bar.css";
> }

This seems valid to me. I know that there are rules around `@import` that we'd need to respect, but I think `@import` should be able to work as the first / only statement of an `@sheet`.

> 3:
> 
> `index.css`
> 
> @import "foo.css";
> `foo.css`
> 
> @import "bar.css";
> Is there a reason we can't do this?

This looks valid to me too.

> 
> I agree that for the order `@sheet` should be valid anywhere `@import` is.
> 
> `<link rel="stylesheet" href="style.css#sheet1"` is this web compatible? Might need a use counter to check if fragments in stylesheet urls aren't already in use.
> 

The CSSWG decided at the F2F last week to not go with URL fragments for binding to `@sheet` identifiers, so we're now looking into adding an attribute to `<link>` tags to specify which sheet is being imported as well as adding an optional identifier to `@import` for similar functionality there (e.g. `@import sheet1 from "style.css";`).

> I know that current frameworks (vite? or vue?) already use this.
> 

That's great context, I appreciate it!

> Anonymous sheets should also be supported.
> 
> @sheet {}
> Only having named sheets makes it impossible to use this when bundling, despite that being one of the stated goals in the explainer.

Could you explain this a bit more? I don't understand how bundling is impossible without anonymous sheets. Also, how could anonymous sheets get applied later? Keep in mind that any rules under an `@sheet` block *don't* get applied by default - they are only applied once they are imported directly by identifier. Without an identifier to be imported later, it seems that these rules would be lost.

> 
> All CSS authors should be able to benefit from this feature, not only those using shadow dom. Most CSS authors will write in multiple files but bundle to avoid a waterfall. They however won't be importing a single sheet. They will want to load and apply everything.
> 
> Having `@sheet` would allow bundlers to avoid other gnarly hacks. Especially when relative imports are mixed with resources loaded from a 3rd party:
> 
> @import "foo.css";
> @import "https://some-framework.com/index.css";
> @import "bar.css";
> The only way to bundle that today is to rewrite as:
> 
> @import url('data:text/css;base64,...');
> @import "https://some-framework.com/index.css";
> @import url('data:text/css;base64,...');
> While with `@sheet` it could be:
> 
> @sheet { /* contents of foo.css */ }
> @import "https://some-framework.com/index.css";
> @sheet { /* contents of bar.css */ }
> Relative urls have some sharp edges, especially when using in custom props. Typed and untyped custom props also have different behavior.

Keep in mind that `@sheet` contents do not apply by default. So for these examples, you *need* an identifier, even if it's throw-away. So your example would look like:

```
@sheet sheet1 { /* contents of foo.css */ } /* Define sheet1 rules */
@import "sheet1";                                      /* Apply sheet1 rules */
@import "https://some-framework.com/index.css";
@sheet sheet2 { /* contents of bar.css */ }           /* Define sheet2 rules */
@import "sheet2";                                      /* Apply sheet2 rules */
```

Does that fundamentally change your bundling scenario? Also, is this something existing `@layer` functionality could help with? I appreciate the scenarios you've provided and definitely want to make this feature useful! I don't have a lot of experience with bundling so this is super helpful context.

> 
> Maybe it makes sense to also have a `@base` rule? To match `<base href="https://www.example.com/" />`
> 
> This `@base` rule could be used by CSS authors but is mostly intended to be inserted by bundlers.
> 
> This would allow all relative urls to remain as they were written without any unexpected behavior differences between bundled or unbundled stylesheets.
> 
> Before bundling:
> 
> `https://www.example.com/public/styles/foo.css` `https://www.example.com/public/images/bar.jpg`
> 
> :root {
>   background: url(../images/bar.jpg);
> }
> When bundled the relative urls will break unless there is some provision:
> 
> `https://www.example.com/public/index.css` `https://www.example.com/public/images/bar.jpg`
> 
> @sheet {
>   @base url("https://www.example.com/public/styles/");
> 
>   :root {
>     background: url(../images/bar.jpg);
>   }
> }
> I think it would be ideal if `@sheet` could be used to fully and accurately represent any graph of `@import` statements in a single file.
> 
> Currently the [landscape for CSS bundlers is pretty bleak](https://github.com/romainmenke/css-import-tests?tab=readme-ov-file#current-state). There aren't many tools that actually qualify as CSS bundlers and the most modern offerings don't even work right.
> 
> Having `@sheet` could reduce the number of hacks needed to implement a CSS bundler correctly. Which in turn would make it easier for CSS authors to pick a tool that actually works.

These are great suggestions and I really appreciate the perspective from someone who has experience with bundlers! It seems like another use case for some sort of a markup-based equivalent of [import maps](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap), and this is close to it. This seems very useful well beyond `@sheet`, so it might make sense to propose as a separate CSS feature.


-- 
GitHub Notification of comment by KurtCattiSchmidt
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/11509#issuecomment-2632432120 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Monday, 3 February 2025 23:47:42 UTC