Skip to content

[@sheet] [AtSheet] solving the duplicate sheet issue. #933

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
trusktr opened this issue Jan 19, 2025 · 4 comments
Open

[@sheet] [AtSheet] solving the duplicate sheet issue. #933

trusktr opened this issue Jan 19, 2025 · 4 comments
Labels

Comments

@trusktr
Copy link

trusktr commented Jan 19, 2025

Problem

With this new @sheet feature, can we please see if we can solve the duplicate sheet issue? The issue is described here:

The TLDR is that when @importing a sheet into other sheets for code modularity and keeping code DRY, what ends up happening is that every @import of a sheet ends up duplicating the sheet (separate CSSStyleSheet instances) which causes each successive duplicate sheet to override any previous customizations in the cascade. The duplicate sheets are visible in document.styleSheets.

This is undesirable because it is unlike EcmaScript modules: unlike with JavaScript import syntax, with CSS @import syntax we cannot rely on multiple imports creating a single instance of a module to share with all dependents that import the module.

To describe it another way, if every time a JS module was imported it made a new instance of the module, it would be highly undesirable and would render ESM highly unusable for specifying dependencies in a manner in which dependencies are localized to the files that need the dependencies. This is the problem that CSS @import has. CSS @import is more like a macro, placing and duplicating the imported style.sheet at that source location.

So, if this will work for JavaScript,

// a.js
import {foo} from './some-sheet.css' with {type: 'css'}
globalThis.foo1 = foo
// b.js
import {foo} from './some-sheet.css' with {type: 'css'}
globalThis.foo2 = foo
// test.js
import './a.js'
import './b.js'

// Expect the sheet to exist once, not duplicated
console.assert(globalThis.foo1 === globalThis.foo2)

then we should ensure that we also have a way to do that with CSS @import syntax for the sake of not only consistency, but for code organizational ability. Without this, we must avoid @import in CSS and instead give end users if a list of all imports they need to write at the head of their app rather than localized in their code files), greatly reducing the DX of authoring CSS.

Solution?

Either it can be fixed with @import sheet(), or a new import syntax is needed for CSS. In hind sight it would have been nice if @import was called @include and that a new @import would have been more modular.

Or is it possible to add a key word like once to @import to opt in? F.e. @import sheet(...) once;. However it seems once (or more accurately ES module) behavior needs to be a default option (or better-yet the only option, see "final thoughts" below as to why).

Example

What do you want this to do?

/* some-sheet.css */
@sheet foo {
  div {
    --some-color: pink;
    color: red;
    background: var(--some-color);
  }
}
/* a.css */
@import sheet('./some-sheet.css#foo');

div {
  color: blue;
}
/* b.css */
@import sheet('./some-sheet.css#foo');

div {
  border: 1px solid var(--some-color);
}
<link rel="stylesheet" href="./a.css">
<link rel="stylesheet" href="./b.css">

<div>hello</div>

<!-- Now is the `color` of the div here blue? Or red? -->

After this series of a.css and b.css files are loaded, the div will be red, not blue.

The solution (whether here or in a separate spec) will result in the div color still being blue, as desired.

The reason this would be good is because now someone can import a.css, b.css, or both, without having to worry about what import dependencies they have (just as with JavaScript modules),

without having to worry about how arbitrary changes to the shape of a dependency graph change cascade ordering.

Note, the new feature would also want to ensure that CSS modules (i.e. which are JS-based singular non-duplicated sheets) are hoisted above all sheets in a module graph. This is the only sane way to ensure sheet ordering is not a problem depending on ordering of dependents in a graph. I.e. the module graph execution order should be king in the same way that JS modules execute once and never again on successive imports.

final thoughts

I think it is important to solve this problem in CSS for code modularity, otherwise the disparity between JS modules and CSS modules will grow, leading to more and more failed expectations as CSS Modules come out in all browsers.

To that end, perhaps we also need @export for the convenience of being able to write CSS Module libs that can re-export from multiple files (with the same semantics as JS modules and non-duplication).

It makes more sense to import functions from somewhere rather than trying to mentally comprehend how functions are overriden based on a cascade. Let's not make things more difficult for people.

If we are careful, having an optional once feature for CSS would align us with well-organized JS and CSS module scripts. Making the behavior match with JS modules will be very good for a consistent future: it will be easy to reason about, and intuitive.

@trusktr
Copy link
Author

trusktr commented Jan 19, 2025

Another way to think about it is that we shouldn't give CSS authors a solution to this problem only in JavaScript, but also in CSS.

@trusktr
Copy link
Author

trusktr commented May 18, 2025

@romainmenke I saw you downvoted. What do you disagree with? What are better alternatives?

@romainmenke
Copy link

romainmenke commented May 21, 2025

I think that having two modes of how imports can work will always be worse than having just the one.

That the current mode has cases where it doesn't do what you want/expect in complex import graphs doesn't automatically mean that another mode will be better.

The other mode will have other cases where it doesn't do what other people want/expect.

I think that another mode will be only that, something that works differently.

I do think that wanting to have a single and shared mental model for how imports work for both CSS and JavaScript is a fair request, but I think that ship has sailed.

I highly prefer having CSS be consistent with itself and have @sheet mirror @import.


Edit: I also don't think there is a real correlation between using @sheet and wanting CSS imports to work differently. I think those are independent concerns :)

@o-t-w
Copy link

o-t-w commented May 23, 2025

IMO I would close this issue as it is confusing to have the discussion in two different places.

While @sheet will probably see @import become more widely used by developers, your issue is not a blocker for @sheet and is only tangentially related to it.

@layer can solve problems created by source order:

@sheet foo {
    @layer base {
        div {
        --some-color: pink;
        color: red;
        background: var(--some-color);
    }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants