w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.42k stars 650 forks source link

Multiple stylesheets per file #5629

Open justinfagnani opened 3 years ago

justinfagnani commented 3 years ago

With Cascading Stylesheet Module scripts on the horizon we will soon have the ability to import CSSStyleSheets directly into JS, like so:

import sheet from './styles.css' assert {type: 'css'};

Problem

The semantics here are fine for unbundled apps, but bundling becomes tricky. If you have two .css files in an app, you can't just combine them. ie:

import sheet1 from './styles1.css' assert {type: 'css'};
import sheet2 from './styles2.css' assert {type: 'css'};

Is not compatible with:

import sheet from './styles1and2.css' assert {type: 'css'};

The current workaround is to compile CSS into JS modules, which defeats some of the performance benefit of having the browser directly load and parse CSS.

Web Bundles might solve this problem generically for multiple file types, though its future on multiple browsers seems unclear right now.

Proposal: @sheet

To fix this and allow bundling of CSS, could we introduce an at-rule that contains an entire style sheet as its contents?

For example, there could be a @sheet rule which allows files to contain named stylesheets:

styles1and2.css:

@sheet sheet1 {
  :host {
    display: block;
    background: red;
  }
}

@sheet sheet2 {
  p {
    color: blue;
  }
}

These could be imported separately from JS:

import {sheet1, sheet2} from './styles1and2.css' assert {type: 'css'};

And also be available on the main style sheet:

import styles, {sheet1, sheet2} from './styles1and2.css' assert {type: 'css'};
styles.sheet1 === sheet1;
styles.sheet2 === sheet2;

Relation to existing approaches

The proposal is most obviously relevant to code that manages CSSStyleSheets in JS - ie, users of Constructible StyleSheets and the API currently named adoptedStyleSheets.

It would also be useful as a bridge to userland CSS loaders that do bundling and scoping via selector rewriting. By standardizing bundling, scoping could be done with client-side utilities:

import {sheet1, sheet2} from './styles.css' assert {type: 'css' };

// doesn't yet exist, but a utility that re-writes class selectors and returns
// an object with a .sheet property and properties for each class
import {scopeSheet} from 'css-module-utilities';

const scopedSheet1 = scopeSheet(sheet1);
const scopedSheet2 = scopeSheet(sheet2);

document.adoptedStyleSheets.push(scopedSheet1.sheet, scopedSheet2.sheet);

document.append(`<div class="${scopedSheet1.fooClass}"></div>`);
document.append(`<div class="${scopedSheet2.barClass}"></div>`);
justinfagnani commented 3 years ago

cc @dandclark @yuzhehan

dandclark commented 3 years ago

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

guybedford commented 3 years ago

If there is a way to add this to the polyfill I would be more than happy to include this in the SystemJS module types polyfill as well. It seems a great feature.

dandclark commented 3 years ago

@justinfagnani A small typo correction for clarity: you've got {sheet1, sheet1} in a few places where I think it should be {sheet1, sheet2}.

justinfagnani commented 3 years ago

Thanks @dandclark! Updated

gsnedders commented 3 years ago

The semantics here are fine for unbundled apps, but bundling becomes tricky. If you have two .css files in an app, you can't just combine them. ie:

import sheet1 from './styles1.css' assert {type: 'css'};
import sheet2 from './styles2.css' assert {type: 'css'};

Is not compatible with:

import sheet from './styles1and2.css' assert {type: 'css'};

Dumb question from someone lacking context about CSS modules: these aren't compatible because sheet1 and sheet2 are both CSSStyleSheet objects consisting of their respective parts, right? Hence you need some way to split up the concatenated stylesheet back up into its constituent parts?

justinfagnani commented 3 years ago

@gsnedders correct

tabatkins commented 3 years ago

This sounds fairly reasonable to me. Semantically, this is basically a more convenient way to write @import url("data:...");, with the potential to hook into CSS Modules a little better.

leobalter commented 3 years ago

cc @bmeck this seems like a good addition for the arbitrary module names, if my bundling still want to reference the names somehow.

leobalter commented 3 years ago

I like this as it also helps on a better usage of dynamic import() for css files, IMO.

devongovett commented 3 years ago

This proposal is very interesting to the Parcel team, and seems to mirror the Module Fragments proposal in the JavaScript world. Current approaches to bundling CSS by simply concatenating them are prone to ordering issues that can cause specificity problems. Sometimes bundling is simply not possible to do correctly due to this. An approach that allows natively combining separate sheets together that can be applied in the correct order would be very useful. 👍

justinfagnani commented 1 year ago

cc'ing @littledan who is working on the related module declarations. If language-specific bundling is needed despite web bundles, maybe we need to push this forward for CSS.

justinfagnani commented 1 year ago

It turns out you can emulate this feature by abusing @supports.

If there's an unknown supports function, the parsed stylesheet will still contain the rules inside @supports {...}. We can invent a function like sheet(name) and stash rules in there keyed by the bundled sheet name:

@supports sheet(styles-one.css) {
  //...
}

@supports sheet(styles-two.css) {
  //...
}

Then we can rewrite CSS module script imports from :

import styles from './styles-one.css' assert {type: 'css'};

to:

import $bundledStyles from './styles-bundle.css' assert {type: 'css'};
import {getBundledSheet} from 'lit/get-bundled-sheet.js';
const styles = getBundledSheet($bundledStyles, 'styles-one.css');

where getBundledSheet is a utility to grab a set of rules in a @supports sheet(name) at-rule and create a new stylesheet out of them.

Proof of concept here: https://lit.dev/playground/#gist=5fab7cc0987e6f1610ba3bd4f432f02c (requires import assertions support to work)


@tabatkins adding native support for something like @sheet would eliminate the processing and double-parse (bouncing rules though .cssText and insertRule()). Seems relatively simple (but famous last words). What do you think about adding this?

dutchcelt commented 1 year ago

This seems like a good progression using assertions. Bundles would become much more useful from CSS perspective.

/* bundle.css */

@layer defaults, common, brand, components;

@sheet designsystemstyles {
  @layer commmon {
     ...
  }
}
@sheet nameofbrand {
  @layer brand {
    ...
  }
}
tabatkins commented 1 year ago

As I said in my previous comment, I think it's a very reasonable suggestion, I just haven't spent any time speccing it out. ^_^

A few questions that probably need resolving, tho:

  1. 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?
  2. 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).
  3. 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.
  4. 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?
  5. 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?
Westbrook commented 1 year ago
  1. Yes.
  2. 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.
  3. Invalid. @sheet should be top level.
  4. Invalid. @sheet should be top level.
  5. 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 do the same, but both feel a bridge to far in a world where we'd actually want something like this to ship...

tabatkins commented 1 year ago

I presume the top-level sheet is the default export, and @sheets just provide named exports, so you can get either or both, depending on which syntax you use for the import.

Crissov commented 1 year ago

Shouldn’t user-defined sheet (and layer) names be either quoted strings or dash-idents?

tabatkins commented 1 year ago

No, not necessarily. They're user-controlled, so there's no need for strings (they can comfortably remain in the identifier syntax). And these names won't mix with other CSS values, so there's no particular need to mark them out as user-defined; plus they're meant to map to JS identifiers, so dashed-idents would be inconvenient.

justinfagnani commented 1 year ago

If the goal is bundling files that otherwise would be part of the module graph, I think some of the questions you raised @tabatkins seem to me to have relative obvious answers, and I would agree with @Westbrook's responses.

The main thing for me is that the module graph is keyed by URL, and so is flat in that sense, so @sheet should be top-level only. References between sheets, should that become possible in the future, should be via a module-graph compatible @import that could reference other @sheets.

tabatkins commented 1 year ago

That makes sense. 👍

tabatkins commented 1 year ago

Agenda+ to check the group's temperature on this idea (and figure out what spec it would go in).

To sum up:

The problem to solve is bundling stylesheets together. (Particularly for importing in a JS import, but also more generally to consolidate multiple requests into fewer, larger requests.) There are some inconvenient workarounds today (@import url("data:...");, a false @media {...} rule that you then reparse the contents of yourself, etc), but they suck, and also don't interact nicely with JS imports.

So for example, given a stylesheet like:

@sheet one { .bar {...} }
@sheet two { .baz {...} }
.foo {...}

then JS can import the sheet like:

import topLevel, {one, two} from "./combinedSheet.css";

Remaining design questions:

justinfagnani commented 1 year ago

Great summary @tabatkins !

When importing a stylesheet using JS imports, while the overall sheet is the default import (how it works today), all the @sheet rules are exposed as separate CSSStyleSheet objects as named exports.

I do want to be careful and conservative about this point, since it would be the first introduction of named exports in a stylesheet module. How might it interact with some of the ideas in https://github.com/w3c/csswg-drafts/issues/3714 - that might create named exports for things like class names like userland modules do today.

I don't think it's much of a concern, since it might be rare to have top-level rules/exports and bundled stylesheets, and even if you did you might be very unlikely to have name collisions, and even if you did have collisions, you're probably generating a bundle with a tool that can handle that by renaming things. But it's something to consider.

justinfagnani commented 1 year ago

fwiw, one reason to expedite this feature would be that if it lands by the time all browsers support import assertions and CSS module scripts, then we might not need to handle cases where browsers support CSS modules but not nested stylesheets. That would simplify using CSS modules scripts in production a lot.

Crissov commented 1 year ago

This may be closely related to @justinfagnani’s comments: it would feel strange to add named @sheet to CSS just so that JS works better, therefore I would expect that CSS’s own @import could make use of sheets to only import select parts of other stylesheets.

Furthermore, could named sheets be exposed as URL fragments then, e.g. <link rel=stylesheet href="style.css#sheet">?

Also, I still like the idea of namespaced custom properties inside variable references from #6099:

@import CD from url(corporate-design.css);
a {color: var(-CD-link-color);}
tabatkins commented 1 year ago

Again, we've historically preserved the single-dash idents for vendor prefixes, and there's no good reason for us to invade that space now. Please stop suggesting it.

Furthermore, could named sheets be exposed as URL fragments then, e.g. ?

Hm, this could be interesting actually. @justinfagnani, how do you feel about that? On the JS side it would still be multiple import statements, but only one request (assuming caching semantics allow reuse of the resource).

justinfagnani commented 1 year ago

Furthermore, could named sheets be exposed as URL fragments then, e.g. ?

Hm, this could be interesting actually. @justinfagnani, how do you feel about that? On the JS side it would still be multiple import statements, but only one request (assuming caching semantics allow reuse of the resource).

This sounds really smart to me. It could work as <link rel=stylesheet href="style.css#sheet">'s inside shadow roots too.

Features that broaden the scope to as many style loading techniques and frameworks as possible sound great to me.

@import (or something similar) between bundled @sheet would be great too, though that may require settling on a semantic for @import in CSS module scripts.

mirisuzanne commented 1 year ago

As mentioned above, I think it's pretty important that a feature this invasive has a CSS use apart from any JS. On the surface, I like the idea of allowing @import/link to access fragments of a larger css file – but I'm curious what the use-case is for that. My immediate guess would be performance optimization, but I'm not actually sure it helps there? Now we're loading a large file, when we only want access to one part of it. But maybe it helps cut down on parse/render time? Is there some other story for how this helps CSS authors write more modular CSS, without relying on JS for the feature to do anything?

calebdwilliams commented 1 year ago

Regarding potential use cases for this feature, this could be very useful in styling things like declarative shadow DOM, especially if there's ever a mechanism for non-constructed stylesheets to be applied to adoptedStyleSheets.

Westbrook commented 1 year ago

For a CSS only usage, I'd say leaning into something like <link rel=stylesheet href="style.css#sheet"> would be one path, though more invasive. This allows the download of all of the CSS that might be needed for an experience in one request without leaving a bunch of unused CSS attached to the page (performance hit) until you need it. Even if the "need it" doesn't come till the loading of a subsequent page, you'll now have a faster load time as the file is cached from the previous request.

@calebdwilliams also hits this on the head for the benefits it brings to declarative shadow DOM. Having a path for a file with multiple @sheets to be including once and then the declarative shadow roots to be able to take parts of that would be a great benefit to that process. This likely requires a little more invasion as it would benefit from something along the lines of https://github.com/WICG/webcomponents/issues/939 as well as ensuring that all CSSStylesSheets were "constructible stylesheets". Each of these could then be addressed later (regardless of their initial acquisition) while also allowing for the form of sub-querying that @sheets would open up a lot of options around how these styles could be used.

css-meeting-bot commented 1 year ago

The CSS Working Group just discussed Multiple stylesheets per file, and agreed to the following:

The full IRC log of that discussion <dael> TabAtkins: There have been a few requests in this space before. Various reasons why you might want a bunch of independant stylesheets and ship as one file. Reduce costs, benefit from compression. Want custom components that relate.
<dael> TabAtkins: Prop in this thread to allow this in CSS. Initial approach focused on JS imports but varient later
<dael> TabAtkins: Prop is a new @ rule @sheet that takes a stylesheet and a name. Syntax inside @sheet is that of a top-level stylesheet. If you use the top level stylesheet directly you only get unnamed sheets. Can access named sheets with fragment on URL
<dael> TabAtkins: When importing or @import you can use the fragment and we'll interpret as a sheet name and grab that one sheet from the larger sheet
<dael> TabAtkins: I think convenient and minimal way to address use case. Thoughts, objections, questions?
<Rossen_> q?
<dael> plinss: What if you have top level entities that aren't @sheets
<dael> TabAtkins: They're part of the top level sheet
<dael> plinss: @sheets are just rules to top level stylesheet?
<dael> TabAtkins: Could go either way. Either OM reflects normally and things in @sheet don't apply or a new list that exposes. Both sound plausable
<dael> plinss: If you use a top-level sheet nested sheets are ignore?
<dael> TabAtkins: Yep
<dael> plinss: Okay. in general in favor
<Rossen_> ack fantasai
<dael> fantasai: Why wouldn't you apply all by default and than option to not apply? I feel like if I was pulling a sheet and it had a bunch of rule expect all to apply
<dael> TabAtkins: Could go either way. I think it makes sense if write as independent so you don't intend them to be used together. But I don't think anything wrong with allowing them to be used together. not sure how parsing of namespace applies in the same sheet
<dael> fantasai: You have ability to apply 2 already. Apply all is extension of that
<dael> TabAtkins: namespace rules should only be sheet they're in
<dael> TabAtkins: Related, any context in the outermost sheet like fontfaces shouldn't apply to the ones inside b/c otherwise it's unpredictable. That leads me to confusing to all apply
<dael> fantasai: I don't think so. It's like I concat a bunch of sheets. That's the premise you started with. @fontface is not to a single sheet. Only namespace is file scope
<dael> plinss: Concerns if we allow all by default you have reules, @sheet, more rules and then you have conflicts where you've got same specificity and what wins. You could end up with situations that surpsise. I'm fine to explore
<dael> TabAtkins: We have limits for that specificity reason. You need to allow imports in @sheet and that introduces that ability we disallowed
<dael> plinss: We'd need to give it more thought
<dael> fantasai: Could just make it invalid
<dael> fantasai: If you have a style rule or @media that's not allowed before @import and you put an @import it's ignroed. We could put that @sheet after something is ignored. If it's out of place it's ignored.
<dael> TabAtkins: That means you can't use @import and @sheet unless it's the first @sheet unless you load independently
<dael> plinss: Clarification, it sounds like you're saying @import @sheet rules @sheet and the first @sheet would be in top level but second wouldn't?
<dael> fantasai: 2nd is invalid
<dael> plinss: Would be ignore
<dael> plinss: Second is I presume @import in the nested sheets that's a fragmenet of the other sheets would work regardless of where it shows?
<dael> TabAtkins: Inclined to say yeah. need to do cycle checking
<dael> plinss: Then you could always say we don't by default but you can import them into the top
<dael> TabAtkins: That makes sense. That's pretty good. WOuld avoid my issues b/c your imports would be loaded as sep. stylesheets at top level
<PaulG> q+
<dael> plinss: Right @import #foo and than #sheet foo and the sheet is hoisted in as a separate document
<TabAtkins> @import "#foo"; @sheet foo {...}
<dael> Rossen_: Is this something that we can summarize as a resolution?
<dael> TabAtkins: Resolve to accept @sheet with URL frag referencing rule. Exact details to be in the spec
<Rossen_> ack PaulG
<dael> PaulG: Coming from outside I think my confusion would be with layers and how this differs. Can layers be used instead of sheets to achieve the import mechanic? My concern is there is two syntaxes doing similar things
<dael> fantasai: Layers effect how rules cascade. Not so much about organizing the style where it is in a file. They change presedence of rules. THis is eq. of @import where you can include rules from one file to another
<dael> TabAtkins: Solely a bunching helper as opposed to layers which impact cascade
<dael> PaulG: Is the concern there when you bring in stylesheet 1 and 2 they can conflict between rules?
<dael> TabAtkins: They do. You can import with a layer to order them and that works here
<dael> PaulG: Not clear on what's being solved for. Purely the addressing of the rules by file orgnization?
<dael> TabAtkins: If you have need for several distinct sheets like custom components in HTML but you don't want n different requests, this solves that problem
<dael> PaulG: Okay, thank you
<dael> plinss: JS import would like you import one of the nested sheers
<dael> TabAtkins: Right
<dael> Rossen_: Prop Resolve to accept @sheet with URL frag referencing rule. Exact details to be in the spec. Additional thoughts or objections?
<dael> TabAtkins: I'm guessing this goes into cascade?
<dael> fantasai: That's what it feels like
<dael> RESOLVED: Accept @sheet with URL fragment referencing rule. Exact details to be in the Cascade spec
sorvell commented 7 months ago

As mentioned above, I think it's pretty important that a feature this invasive has a CSS use apart from any JS.

If there were an accompanying "apply this sheet here" @rule, it could effectively become a "mixin."

@sheet redMixin {
  :scope {
    background: red;
  }
}

.myContainer {
  @apply-sheet redMixin;
}
mirisuzanne commented 7 months ago

For full mixin functionality, you would want the ability to pass in arguments.

EisenbergEffect commented 7 months ago

I believe that this feature also provides a way to handle a recent set of community requests that have surfaced around component customization.

If Web Component library authors distribute their CSS in a single file with @sheets for each component, then it will make it very easy for component consumers to provide custom styles or override styles of the 3rd party components. They simply replace the external CSS file provided by the library with their own. Import maps can be used if needed.

LeaVerou commented 6 months ago

I’m wondering if @export would be a name more consistent with the rest of the web platform. I was confused when I first saw @sheet, but at least to those who write JS, export is a familiar concept. We already have @import, so this would be nicely analogous.

justinfagnani commented 6 months ago

@LeaVerou I would think that @export should be used for a more general export mechanism that might export selectors or class names, rulesets, upcoming mixins and functions, etc. This feature is for defining an independent nested stylesheet ,so @sheet describes what it is, and they're just exported implicitly because they're not useful when not exported.

If a sheet should be explicitly exported, maybe the syntax should be @export @sheet?

@export @sheet sheet2 {
  p {
    color: blue;
  }
}

I'm not sure if @-rules can work that way though.

LeaVerou commented 6 months ago

@justinfagnani I’m not sure what a mechanism to export selectors or class names would look like. Rulesets, mixins, functions can already be exported with this rule since its contents would be <stylesheet>, no? From an architectural pov, I would be wary of introducing features in the web platform to do things that are seen as similar but slightly different, as that tends to cause confusion and poor DX.

Westbrook commented 6 months ago

From an architectural pov, I would be wary of introducing features in the web platform to do things that are seen as similar but slightly different, as that tends to cause confusion and poor DX.

This is a worthwhile line of investigation, and would likely benefit from actual user testing or surveying, etc. Historically, we've done import/export within the JS context, but neither CSS nor HTML have done this bi-directionally, even in their own context, to the best of my understanding. As we expand the use of Import Attributes, it'll be good to take a stance on whether that should be more like something that exists or more clearly a new feature. That hopefully means that an answer here could be applied to the conversation around with { type: 'html' }, too.

With this in mind, I wonder if the use of @export would be more confusing as it is not reciprocal to the @import token that is already available in CSS?

justinfagnani commented 6 months ago

@LeaVerou

I’m not sure what a mechanism to export selectors or class names would look like

Some ideas are in here, though without the @export keyword: https://github.com/w3c/csswg-drafts/issues/3714

Rulesets, mixins, functions can already be exported with this rule since its contents would be , no?

What if you wanted to import a single mixin and apply it in a specific place, not a whole stylesheet?

mayank99 commented 5 months ago

Does this feature open up the path to real "CSS modules"?

Right now, all CSS imports are side-effectful, but @sheet could make it possible to split the "import" vs "apply" parts into two steps.

  1. Declare:
    /* In foo.css */
    @sheet A {…}
    @sheet B {…}
  2. Import:
    @import "foo.css" sheets(A, B);
  3. Apply (in any order desired):
    @sheet B;
    @sheet A;

Also it would be useful to be able to rename sheets when importing. I think this functionality is essential for any module-like system.

@import "foo.css" sheets(A as X, B as Y);

@sheet X;
@sheet Y;

Lastly — and this might be far-fetched — maybe there should be some reserved sheet names that have special meaning. For example, @sheet inherit; could be used to reference host context stylesheets from within a shadow-root. This could be a solution for open-styleable.

<head>
  <style>
    @sheet A {…}
    @sheet B {…}
  </style>
</head>

<div>
  <template shadowrootmode="open">
    <style type="module">
      @sheet inherit.A;
      @sheet inherit.B;
      /* or just @sheet inherit; */
    </style>
  </template>
</div>

Regardless of the specific ideas and syntaxes I illustrated, I think we should all be thinking about how to make CSS (and HTML) better and more useful on its own, even when JS is not involved.

justinfagnani commented 5 months ago

Right now, all CSS imports are side-effectful

CSS imports only return a CSSStyleSheet object - they don't have any side-effects.

mayank99 commented 5 months ago

I'm talking about CSS @imports (in CSS), rather than CSS module script imports (in JS).

o-t-w commented 5 months ago

With this in mind, I wonder if the use of @export would be more confusing as it is not reciprocal to the @import token that is already available in CSS?

100% agree - I would naturally assume I could @import something I have @exported, although from the CSSWG meeting transcript it looks like you will be able to to do this so maybe the export name is ok (although I've not seen anyone answer Miriam's question about whether being able to @import an @export is even a useful thing to be able to do, whereas the JavaScript use case is very compelling by itself). Because the thing getting exported is a CSSStyleSheet I find the @sheet syntax more clear regardless.

@export @sheet

This syntax is ugly imo. Particularly if you are using other @rules like @layer within the sheet, its too many @s that it feels a bit overwhelming. It would be unfortunate to adopt an unnecessarily verbose syntax just for the purposes of catering to a totally speculative potential future CSS feature.

mayank99 commented 5 months ago

In my previous comment, I described a CSS way to import an existing @sheet from another file.

Another useful thing would be the ability to name a regular stylesheet from HTML, using an attribute like name (similar to <link title="…">).

<link rel="stylesheet" href="legacy.css" name="legacy" />

This would give you a @sheet named legacy can be referenced elsewhere.