Open mayank99 opened 7 months ago
In general, "special syntax to make something go first" isn't very composable. Things end up fighting over being first, the set of things putting themselves first still needs to establish a relative ordering. You just kick the bucket of "it's hard to coordinate" a little bit down the line, but not very far.
On the other hand, we have had multiple proposals for something like !default
as the inverse of !important
, just a way to downgrade a style to be weaker than all "normal" styles.
So this might indeed be reasonable, just establishing that there's a specific layer that's always defined and before all named layers. Some specific comments:
I don't think named layers need an "initial" sublayer. By definition, you're in control of the named layer; you can set up your sublayer order immediately. The only possible concern is that someone else is using the same layer name and so you're fighting over the namespace, but that just means you should use a different name.
It can be useful when the top layer is set up by someone else, such as when a stylesheet is imported into a layer. It would be confusing if the behavior of initial
changes depending on how the CSS is imported.
// In foo.css
@layer A, B;
@layer A {…}
@layer B {…}
@layer initial {
/* expected to go before A and B, normally */
}
@import "foo.css" layer(foo);
/* foo.initial now comes after foo.A and foo.B 🙁 */
Of course there is a workaround, so maybe not a big concern.
@layer initial, A, B;
I was thinking about this again, and I'm starting to wonder if we need something like @context
to allow authors to place styles before or after the document and shadow contexts.
Let's think about CSS resets again. Most sites do something like this today:
*, ::before, ::after {
box-sizing: border-box;
margin: 0;
}
This has low specificity and can be de-prioritized further using @layer
. But it still takes precedence over the previous contexts. This is a problem because it makes it hard to write styles using :host
and :slotted
selectors.
<my-component>
<template shadowrootmode="open">
<style>
/* This gets overridden by our reset 🙁 */
:host { margin: 1rem }
</style>
<slot></slot>
</template>
</my-component>
What if we instead allowed authors to put styles in a context that precedes all other contexts? This would be the ideal place to put resets and default styles.
@context defaults {
*, ::before, ::after {
box-sizing: border-box;
margin: 0;
}
}
To take this one step further, it would be equally useful to have a context that comes after all other contexts. This would be a great place for e.g. browser extensions to put user styles (see #7535/#6323). For this to work, we'd need either pre-determined names or a different at-rule.
@context(first) {
/* resets/defaults go here */
}
/* all regular styles will fit in between */
@context(last) {
/* user styles go here */
}
There's a lot of bikeshedding opportunities, but hopefully the idea is clear: @context
would be a thing that lives one level above @layer
. This also means @layer
can still be used to organize styles within each context.
I find the @context
specifics a bit hard to track - maybe because I don't usually think of page/shadow contexts being ordered. But I suppose it's reasonable to say the order is something like:
So your proposal would result in…
But if styles can be added to a first/last context from either the light or shadow DOM… does their originating context still come into play? Or do we now ignore the shadow/page context, and conflicts within first/last have to continue in the cascade?
@layer
rule. context
, since styles would now move across contexts no matter where defined?@mirisuzanne Thanks for looking into this. If you think this idea has legs, should I open a new issue specifically for @context
?
But if styles can be added to a first/last context from either the light or shadow DOM… does their originating context still come into play? Or do we now ignore the shadow/page context, and conflicts within first/last have to continue in the cascade?
I want to say the originating context should not matter because it's a totally different context. The alternative would be that @context
styles added from the innermost shadow context would cascade first.
In https://github.com/w3c/csswg-drafts/issues/6323#issuecomment-2181121893, I was also wondering if maybe @context
should not be allowed to be used from shadow context. This sidesteps some of harder questions and can be reasoned about since the shadow styles are already in a fully encapsulated special context. Although, I think it would be confusing if some CSS features literally did not work in shadow DOM.
As a solution to static top/bottom layers… I would rather see that built into the
@layer
rule.
We could also have something built into @layer
. The @context
proposal does not have to replace my @layer initial
proposal or any of the !override
-like syntaxes proposed in #6323.
I do see the additional use of specifying something akin to presentational-hints, but in my mind this confuses the current meaning of a
context
, since styles would now move across contexts no matter where defined?
I only picked "context" because it seems like the right layer (no pun intended) to place "first"/"last" styles. What would be other ways of solving the problem I described in my earlier comment? Is it possible to make @layer initial
cascade before the shadow context? Maybe @layer initial
should actually refer to the presentational-hints layer?
Based on https://github.com/w3c/csswg-drafts/issues/6323#issuecomment-2207341923, I guess the original proposal in this thread could also be renamed to something like @layer !defaults
. The only downside is it's not possible to easily polyfill.
I still think the @context
idea solves something that the original proposal doesn't currently solve. Ideally, @layer !defaults
would be placed alongside or prior to the presentational hints layer, so that it cascades before the shadow context.
It makes sense to place it before presentational hints, because I expect default styles to have the lowest priority in the author origin, and be overridden by preshints. As an added benefit of this, the behavior of revert-layer
on the first non-default layer would remain the same (i.e. it doesn't revert preshints). I'm just not sure if it's possible from an implementation perspective.
Problem
Currently, layers all need to be explicitly defined. This means the page author needs to be very disciplined in setting their layer order upfront, before adding any “real” styles.
This almost always involves creating a layer named something like “defaults” early on.
This works, but is problematic for a few reasons:
@layer defaults
can potentially declare layers that come first.defaults
if they want to declare low-priority styles later.defaults
layer.Proposal
Allow authors to declare all low-priority styles in a layer named
initial
.initial
is one of the layer names that are "reserved for future use", so this layer can be automatically set up by the browser, such that:@layer initial
exists implicitly, regardless of the page’s layer order, similar to the implicit “outer” (unlayered) layer.Polyfill
In my testing, I found that the three major browsers do not do anything special to the “reserved” layer names, even though the spec says these must be invalid at parse time. I initially thought this was a bug and/or maybe the spec should be changed (see #10067), but then I realized that the current browser behavior makes this feature somewhat easy to polyfill, by setting
@layer initial;
as the very first style. This could even be done automatically by frameworks.Use cases
This solves all of the problems described above, and makes cascade layers easier to incorporate into existing workflows.
initial
layer, without having to come up with a carefully-coordinated layer setup.@layer initial
will provide a canonical way to deprioritize certain styles. It "always works" and is not impacted by order of appearance or existing layer architecture.@layer initial
, with a guarantee that they will cascade first, regardless of how/where they are imported.initial
layer.@layer initial
for their default styles, even though.adoptedStyleSheets
is ordered after the inner context’s own.styleSheets
.initial
layer will still cascade before the shadow-root's layered and unlayered styles.Open questions
Should
initial
also be implicitly set up for nested sub-layers? (I think yes)initial
?This one is interesting. I think it would be useful ("lowest of the lowest priority") and it would match how the outer implicit layer contains author-defined explicit layers.