Skip to content
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

[css-cascade] Proposal: @layer initial should always be the first layer #10094

Open
mayank99 opened this issue Mar 16, 2024 · 2 comments
Open

Comments

@mayank99
Copy link

mayank99 commented Mar 16, 2024

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.

<head>
  <style>
    @layer defaults, components, utilities;
  </style>
  <link rel=“stylesheet” href=“styles.css” />
</head>

This works, but is problematic for a few reasons:

  • If this layer order is not set upfront, then any styles that come before @layer defaults can potentially declare layers that come first.
  • It requires everyone to be aware of the name defaults if they want to declare low-priority styles later.
  • It hinders the adoption of libraries that want to use cascade layers, because they need to instruct page authors to set up layer order correctly.
  • Shadow-roots need to individually come up with their own defaults layer.

Proposal

Allow authors to declare all low-priority styles in a layer named initial.

@layer initial {
  *, ::before, ::after {
    box-sizing: border-box;
    margin: 0;
  }
} 

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:

  1. It is always the first layer that comes before all other layers.
    • It is always the first within a context (e.g. shadow context vs host context).
  2. It always exists automatically, i.e. it doesn’t need to be explicitly created.
    • This means @layer initial exists implicitly, regardless of the page’s layer order, similar to the implicit “outer” (unlayered) layer.
@layer A, B;

@layer initial {
  /* this will cascade before A and B */
}

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.

<head>
  <style>@layer initial;</style></head>

Use cases

This solves all of the problems described above, and makes cascade layers easier to incorporate into existing workflows.

  • Page authors and component authors alike can easily add styles to the 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.
  • CSS “resets” (such as “CSS remedy”) can be pre-wrapped in @layer initial, with a guarantee that they will cascade first, regardless of how/where they are imported.
  • Component library authors can use CSS layers more liberally, without having to worry about third-party resets potentially being in higher-priority layer.
    • Page authors may not even need to care whether a library’s CSS uses cascade layers internally.
    • Library's CSS will be able to use an anonymous layer, with a guarantee that it will still cascade after the initial layer.
  • Outer context styles that are adopted into an inner context can use @layer initial for their default styles, even though .adoptedStyleSheets is ordered after the inner context’s own .styleSheets.
    • This is particularly important for open-styleable shadow roots, where document stylesheets may be ordered after shadow-root’s styles. The document's initial layer will still cascade before the shadow-root's layered and unlayered styles.

Open questions

  1. Should initial also be implicitly set up for nested sub-layers? (I think yes)
    @layer foo {
      @layer A, B;
    
      @layer initial {
        /* comes before foo.A and foo.B */
      }
    }
  2. Should authors be allowed to add sublayers into initial?
    @layer initial.foo { … }
    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.
@mayank99 mayank99 changed the title [css-cascade] Proposal: @layer initial always being the first layer [css-cascade] Proposal: @layer initial should always be the first layer Mar 16, 2024
@tabatkins
Copy link
Member

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:

  • sublayers of this initial layer are absolutely required. Nothing about an initial layer negates the need for organizing styles within the layer.
  • 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.

@mayank99
Copy link
Author

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;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Cascade 5 (Layers)
Awaiting triage
Development

No branches or pull requests

2 participants