Description
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 @import
ing 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.