Open mirisuzanne opened 3 years ago
Definitely welcome to have that level of control. If you didn't want a reserved name you could use different @ name. e.g. @layer reset, framework; @unlayered; @layer components, utilities;
Idk if it's a good idea, but one possibility would be to just leave out the identifier.
@layer reset, framework;
@layer;
@layer components, utilities;
Thinking about this a bit more: every layer (including but not limited to the default/root layer) has the potential for both direct style-rules and nested sub-layers. So this feature might be useful in nested context, not only in the root/default situation:
@layer one;
@layer;
@layer one {
@layer two;
@layer;
}
On the other hand, the root/default layer is the only place where authors might not be able to add explicit layering – for the sake of backwards compatibility. Once styles are layered, there is no harm in layering them further. So from that perspective, control is only needed for fully-unlayered styles.
Is it confusing if authors can specify different defaults inside each layer context? Is it more confusing if this only works at the top level, and does not work in nested contexts?
It would be a shame in my opinion if it didn't work the same in nested layers.
I'm also not sure how @import url(links.css) layer(mylayer);
would work if @layer;
didn't work the same when it becomes nested by the import as it does when the import doesn't inject it into a layer.
The CSS Working Group just discussed Allow authors to explicitly place unlayered styles in the cascade layer order
, and agreed to the following:
RESOLVED: Reserve the CSS wide-keywords (making the whole layer block invalid at parse time) for now and details TBD when we have better use cases
Given the resolution above, I think we all agree that @layer initial { }
is simply invalid and the whole block is rejected. However, what of @layer foo, initial, bar;
? Is that whole rule rejected too, or do we simply order foo and bar and ignore the non-existing initial
layer?
I'm not sure, but I suspect making the whole rule invalid is safer. Otherwise, we might have people who introduce a @layer initial { }
block, fail to notice that that doesn't do anything, order that layer into the middle of the stack with @layer foo, initial, bar;
, and some day, if we do make the initial
keyword apply in that situation, that changes the ordering of their whole page. I think this scenario would be less likely to happen if we ignore the whole rule, as then the author would be confronted with the fact that the ordering of their foo
and bar
layer don't work either, making it easier to notice.
I'm happy with that approach, and drafted some spec language around it. Not sure if we need to get an official resolution, or not?
I noted while thinking about this in the context of #6284 that explicitly pinning "initial" as a layer name isn't very convenient, because this has to happen once, and so that first declaration needs to be aware of all the layers that need to be above or below the initial layer (which might not be possible if you are using themes / unrelated add-ons).
Another approach I have been thinking about is to have two lists independently, all layers that must be above the unlayered styles, and all who need to be below.
A strawman would be as follow (`!important following an at-layer name means to put in the list after unlayered styles):
@layer reset { article h1 { margin: 0; } }
@layer theme { h1 { margin-top: 1.2em; } }
@layer special-overrides !important { h1:first-child { margin-top: 0 } }
.main-title { margin-top: 0.2em; } }
That would yield the following order for the layers : reset < theme < /initial/ < special-overrides
.
Adding !important
to individual values has an effect as usual, the !important
on @layer
only changes the order of layers in the list, but does not propagate to the values themselves (but the change in order for the layer will make the declaration be more important).
The advantage I see is that we are not prescribing anything here about the specifics of the default, authors can get one or the other depending on their needs.
I think it's going to be useful and important to be able to have one-off layer declaration blocks that are above or below the default-layer styles, so we should have a syntax built into the @layer rule that says whether it goes above or below. Something like:
@layer [ up | down ]? <layer-name> { ... }
where
up
increases the priority of normal rules (and decreases priority of !important rules) down
decreases the priority of normal rules (and increases priority of !important rules)I like the goal here, but have a few questions.
As I understand this, we would basically be creating two layer stacks — one above and one below the default — and then use the keywords to append layers to the top of either stack? What's the result of these cases?
@layer up one;
@layer down one;
Does that give us two layers with duplicate names (upper one & lower one)? Or do we only allow this keyword when the name is first used (in which case the second rule is invalid)? Another option is that we only provide this one-off syntax for truly one-off unnamed layers? In which case we likely need both the explicit placement, and the one-off option.
@layer <layer-name> | [ up | down ] { ... }
I assume we don't want to allow moving layers around retroactively, so the second rule should not impact the layers defined in the first rule. That's what up
/down
imply to me. So if that's not what we mean, I think we should name the two layer stacks, and use their names as the keywords: something more like upper
/lower
or default
/important
or …?
Given my proposal is to have two independent lists, I guess it's fine to have two layers named "one", one in each list.
Another way of seeing this is that one layer would be named "one" and the other "one!important" without préjudice to each other.
(Or, you know, "up one" and "down one" if we adopt fantasai's syntax)
I'm not a big of this "two lists" idea - it makes ordering less obvious (no longer order-of-appearance, but two simultaneously-calculated orders of appearance), and it brings up identity questions like what @FremyCompany referenced without good answers.
"One-off" layers that are before/after a specific significant layer are already something authors might want for any layer, but we don't allow that because it's better for the page to declare its layer order up-front; it makes the entire feature more understandable and manageable over time. I don't think the default layer is in any different in this respect.
(We do at least let styles arbitrarily inject themselves after a given layer via nesting; an @layer foo.bar{...}
comes after all the "foo" layer styles. We could allow nesting under initial
(assuming that's what we call the default layer) to let people achieve some of this, in a way that's consistent with any other layer.)
Why not. I would be totally fine with
@layer initial.overrides {
... /*above unstyled*/...
}
But then maybe "initial" is a strange name for that layer. What are our other options? Unset?
I don't follow that.
@layer initial.overrides {... }
is the same as
@layer initial {
@layer overrides { ... }
}
except that
@layer initial { ... }
is implicit, so is the same as
@layer overrides { ... }
which is (now) an underpin (down) layer, not an overrides (up) layer.
No, the point is that, because the initial layer is explicitly named, it's not implicitly removed from the layer stack.
So one could do @import url("tabs.css") layer(initial);
and all internal layers defined within tabs.css would then override the unlayered rules in the same file?
I suppose that would be the implication, sure?
Another possibility is to add another layer that is not the unstyled layers but is one just after it. Then confusion drops a lot I guess, especially if we give it a better name.
Something like @layer !important.xyz { ... }
where the !important
layer is a special layer that is above the unstyled styles.
I like the !important
direction - it serves the same purpose as using initial, but adds a bit more clarity about what's happening.
(Except that !important
declarations in the !important
layer would have lowest priority. Something something two wrongs make a right…)
All these approaches get a bit messy when we put them into a nested context — which @alohci pointed out is un-avoidable. The keyword option only works if we nest entire blocks, but becomes semantically unclear when using the nested name syntax:
@layer up framework {
@layer down defaults { … }
@layer up components { … }
}
/* how does this utilities layer relate to framework.<unlayered>? */
@layer up framework.utilities;
The implicitly named override layer (e.g. !important
) can be repeated at each level, but the result is verbose:
@layer !important.framework.!important.utilities;
After a conversation with @fantasai, we're proposing a variation on the original proposal, using the initial
keyword in layer list declarations. But rather than placing it once, like other layer names, we would treat it as a relational anchor for the other layers in the list. The following declarations can be folded together:
/* each use of initial is scoped to that layer rule */
@layer reset, initial, utilities;
@layer defaults, initial, overrides;
/* the result maintains relations to initial */
@layer reset, defaults, initial, utilities, overrides;
There is still a single layer stack (and shared namespace) for layer names at any level, and layers still default to placement before/under the initial
layer, unless placed after initial
in a list. Authors could also use a convention to achieve more explicit under/over layer names, if desired:
/* can be defined at any level of nesting */
@layer down, initial, up;
/* or even described in the nested syntax */
@layer up.framework.initial, up.framework.up.utilities;
There is still potential for name collisions that change the intended layer order, but those collisions are limited to author-defined layer names (which can already collide). In this case we would want to follow established conventions for name collision — such that the first mention takes precedence. For example:
@layer initial, framework;
@layer framework, initial;
Results in a single framework
layer above the initial
unlayered styles. The inverse can also be resolved using our existing rules. Any of these options result in framework
being placed below initial
, since the first mention of framework
has it placed either explicitly or implicitly below:
/* single list */
@layer framework, initial, framework;
/* split v1 */
@layer framework;
@layer initial, framework;
/* split v2 */
@layer framework;
@layer initial, framework;
Note: Given that approach, the following are meaningless, and should be treated as invalid:
/* nothing is being anchored */
@layer initial;
/* can't have two anchors in one statement */
@layer initial, framework, initial;
/* initial is not a layer that accepts sub-layers */
@layer initial.resets;
We think the combination of a list-anchoring syntax, combined with the option to create more explicit conventions, should cover the majority of use-cases — but we'd love to get more feedback here. Especially from authors.
Sounds doable as well. But this begs the question: why make initial
the only layer you can order relative to?
@layer base, theme, initial, overrides;
...
@layer base, base-overrides;
(with order base, base-overrides, theme, initial, overrides)
@FremyCompany If you applied our proposal to base
in this case, it would actually result in an order of base, theme, initial, overrides, base-overrides
. We're not anchoring directly next to the initial
layer, but on a given side of it. Otherwise the names are still stacking in the order they are encountered.
But your outcome can already be achieved with nesting:
@layer base, theme, initial, overrides;
...
@layer base.initial, base.overrides;
Okay, so initial wouldn't be a "layer", per se, just an indicator of whether a given (top-level?) layer is in the "before unlayered" or "after unlayered" lists.
I still don't think I'm a fan of this. It still presents what I consider a confusing situation, where the layers are not in order-of-appearance, but rather maintain two completely separate orders of appearance. I continue to question why we want to allow this, when multiple @layer statements aren't a great idea anyway, since they can't control their interweaving. It's a good practice to declare all your layers (the top-level ones, at least) in a single @layer statement.
Like, I think this:
/* each use of initial is scoped to that layer rule */
@layer reset, initial, utilities;
@layer defaults, initial, overrides;
/* the result maintains relations to initial */
@layer reset, defaults, initial, utilities, overrides;
is a confusing result, and (rightly!) not something that can be achieved relative to any other layer.
I still think it's best to go with with the simplest possible solution: initial
is a layer name that refers to the unlayered styles, and is reserved to only be usable at the top level. Just like any other layer name, it takes its position from its first mention; if not mentioned, it goes first or last (I forget which way we've settled on). Anything else is adding functionality to the initial layer that no other layer has access to, nor do we want to give them access to, and I don't understand why the initial layer is special enough to need that.
(As I said in an earlier comment, we could still allow the initial layer to have nested layers like a normal layer, but we don't need to.)
I agree that we should keep things as simple as we can. And if we can avoid too much special-casing, that's great. Yes, best practice is likely to be ordering as much as possible up-front.
But we've explicitly said that one of the goals is making it easier to consume third-party styles, and slot them in. That means we will often be dealing with stylesheets that have different assumptions about internal layer ordering. So far that's ok, because you can consume an external file while also nesting it inside a layer. For that to continue working, we have to handle the situation where framework.css
is designed with unlayered styles in the middle, but is then nested into the specific layer of a site's styles.
So I'd be ok with "initial is just a reserved layer name" – but not if that behavior is restricted to the root layer. Allowing it to be nested is the only way to make it work along with the existing logic. After a conversation with @jensimmons, she also liked that approach, but proposed unlayered
as a more clear keyword.
(I understand the reason people have been pushing for additional special-casing – but I think "nesting namespaces" has been our proposed solution for other potential naming conflicts, and I believe we can apply it here as well.)
So I'd be ok with "initial is just a reserved layer name" – but not if that behavior is restricted to the root layer. Allowing it to be nested is the only way to make it work along with the existing logic.
Does this mean that when nested, initial
refers to the styles in that layer that aren't further nested into sub-layers? Or something else? (An example would probably help.)
Yes, it would refer to styles that are not further layered. Given the following, and an element with class='btn green'
, the default will be maroon
(at each level, initial styles take precedence):
.btn { background: maroon; }
@layer framework {
.btn { background: rebeccapurple; }
@layer utility {
.green { background: green; }
}
}
But if we want framework.utility
to take precedence, we can do it by re-ordering initial layers. Either once up front:
@layer initial, framework.initial, framework.utility;
Or defining the order for each level of nesting:
@layer initial, framework;
.btn { background: maroon; }
@layer framework {
@layer initial, utility;
.btn { background: rebeccapurple; }
@layer utility {
.green { background: green; }
}
}
All right, yes, I'm in favor of that. Simple and understandable, +1.
My concern here is that a layer (whether named or anonymous) that is not accounted for in the first statement that mentions initial
has no way to pull itself ahead of the unlayered rules. We decided to have a default behavior, and that's fine, but you can't actually change the default behavior without naming the layer and listing it at the top of the file. We decided not to require all layers to be named up front, so I think wanting a different default behavior for layer precedence shouldn't impose that requirement.
Also, in every other case, if you take a bunch of separate CSS files containing @layer
rules and combine them into a single file, they behave the same way as if you imported them in that order, as long as you avoid naming clashes. But initial
is guaranteed to name-clash, so treating it as just a regular layer with an automatic name means it's effectively ignored in later rules, and that breaks this invariant.
Nesting is not a solution to this problem because you might want all of the unlayered styles to cascade together. Nesting is not merely a namespacing mechanism, it changes the cascade also.
My concern here is that a layer (whether named or anonymous) that is not accounted for in the first statement that mentions initial has no way to pull itself ahead of the unlayered rules.
Yes, but that's true of every other layer, too. If you have a foo
layer early in the order, a later bar
layer is unable to ever put itself before it unless it's explicitly named as such in a preceding @layer bar, foo
rule. What's special about the initial layer such that it requires us to favor it here?
Remember that the intention is that if you're using layers, ideally all your styles are layered so you have full control over the ordering; we need to handle the initial layer just to help with adapting an existing unlayered codebase into using layers. It's not meant to be special in any particular way otherwise.
Agenda+ to see if we can get a resolution on this. I think it's the final blocker keeping browsers from shipping this feature.
The more I look at it, the more convinced I am about taking the simple approach here: with a named initial
layer that can be ordered like any other layer. We always want to give the final site author control over the final layer order, and any attempt to have layers declare their own position relative to the default goes against that goal. Frameworks should not be able to place themselves above or below the initial layer. The final author should make that decision by establishing layer order up front.
I lean towards keeping this simple. The up
down
thing feels over engineered to me.
I also don't like the word initial
. It may make sense to folks on the CSSWG, but to the outside world of web developers, initial
means browser defaults. Their unlayered styles are not the initial styles. The UA styles are the initial styles. I confirmed this with a name-bikeshed bait tweet.
I believe unlayered
or some other word that means “outside a layer” would be better. By using the word “layer”, we make it easier for non-English speakers to pair the two words together: @layer
and unlayered
or (similar).
@jensimmons Wrt syntax, I'm quite open to other keywords. We could alternatively use a slash:
/* layered below unlayered (default) */
@layer foo;
/* foo, bar, unlayered, baz */
@layer foo, bar / baz;
/* bar above unlayered */
@layer / bar;
The thing that's important to me is that when you import a stylesheet using @import or import it using a server-side include, it doesn't unexpectedly re-interpret the cascade order of things within your style sheet.
If we do go with a two-list option (above and below unlayered styles), I like the idea of doing it with a slash instead of a keyword.
The CSS Working Group just discussed explicitly place unlayered styles into the layer order
, and agreed to the following:
RESOLVED: Reject this proposal; unlayered styles have a specified location in the layer stack which can't (currently) be controlled
With apologies for blowing new life into this issue, but after getting several questions about this (in replies on posts/tweets or during a Q&A after a talk) I feel that people need a solution to this problem.
To summarize the problem: unlayered styles win from layered styles and that is …
@layer
will ignore the layered styles. This way you end up with a bunch of layered reset/3rd-party styles not being loaded at all in those browsers. Author unlayered styles then get applied on top of nothing, which is like building the tenth floor of a flat but with the foundations and nine floors below it missing. Weird. 😩Getting people sold on that 2nd item is very hard (and until now impossible) sell to be honest. My message right now is that while layers are great, you can't really use them in a PE-mindset:
@layer
style-less.@layer
.This is a message I don't really like to bring?
Summarizing this issue, I saw syntax proposals to:
up
and down
where proposed) or using a /
initial
, unlayered
, etc.)I would like to request the CSS WG to look into these syntax suggestions and to provide a structural solution. That way we can have both Layers and keep Progressive Enhancement in mind while building websites.
Hi @bramus,
Having layered styles always win is not going to make your site work in browsers that don't support layer if you don't do anything, I fail to see how this would be of any use at all to fix the Progressive Enhancement problem. All styles must work for a site to work, not just some.
There is no other solution, people who want to use @layer
today can, as long as they use a (compile-time) polyfill.
Here is the conversion that is required:
/* this style will win even if this is the first h1 because it's unlayered */
h1.main { color: green; }
/* these styles are like defaults */
@layer x {
article > h1:first-child { color: red; }
h1 { color: blue; }
}
=>
/* layer styles must come first, with :where so they have no specificity and get overriden by all other styles */
/* this means that the rules have to be sorted by specificity per layer, otherwise this won't work */
/* these styles are like defaults */
:where(h1) { color: blue; }
:where(article > h1:first-child) { color: red; }
/* unlayered styles can come in regular order, after the unlayered styles */
/* this style will win even if this is the first h1 because it's unlayered */
h1.main { color: green; }
I agree with @FremyCompany; changing the order won't help you much, if at all, when @layer rules are discarded in legacy browsers. This is something that should be addressed with a CSS compiler, if that's a need, and luckily the feature that makes it reasonable to do so (:where()
) is older than layers (but you can do it even without :where()
, it just requires even more work from the compiler).
What I had in mind was the inverse of current promoted approaches, namely: keep the resets and base styles unlayered, and instead layer up the rest of the author styles (giving them a higher priority than the unlayered styles).
That way:
@layer
get a basic set of styles (i.e. reset.css + base.css)@layer
get the full experience (i.e. reset.css + base.css + [layered] styles.css)This approach wouldn't require any jumping through hoops by authors, nor the use of a build step. It just works!
With this, an author can also decide for themselves whether they want to layer 3rd-party styles or not. Should the specificity of the 3rd-party CSS conflict with their own styles, then it would be recommended to do so.
Right, @FremyCompany's point is that this wouldn't, in any meaningful sense, "just work". You're still missing all your layered styles.
It seems like you're operating on the theory that this is similar to JS and "progressive enhancement", where you can design a basic experience using older tech (like writing an app using <a>
s and several distinct pages) and then layer new fancy tech on top for a better experience in new browsers (like using fetch()
to turn it into a single-page app). That's not the case here; layers aren't cool new tech, they're organizational. The stuff you put in layers is identical to the stuff not in layers; there's no dividing line to draw in terms of "enhancement".
This is much more similar to switching to JS arrow functions over the old function
declarations; the code is identical in behavior either way, it's just more convenient to write with the new stuff. You wouldn't separate out your code into "basic" stuff using function
and "new, better" stuff using arrow funcs that's okay to be skipped in older browsers. You'd either use function
everywhere, or use arrow funcs and a JS compiler to turn it into old-browser-compatible code, or just wait until arrow funcs are widely supported and ignore the minority of older browsers.
This wouldn't, in any meaningful sense, "just work". You're still missing all your layered styles.
In some cases missing the layered styles could still give you an acceptable result. I don't wholeheartedly agree with the “All styles must work for a site to work, not just some.” statement as a site can work just fine without any styles at all.
But OTOH, yes, I can also see that in other (many? most?) cases it would be feasible to have all styles loaded.
This is much more similar to switching to JS arrow functions over the old function declarations […]
I see. Reasonable explanation. Thanks, for enlightening me on it.
Layers aren't cool new tech
Harsh! 😛🙃
Yeah, @bramus your argument is exactly the one that took us back and forth on this a few times - but I think Tab is right that once we accept there's no dividing line to draw in terms of "enhancement"
, then there are several advantages to making it so 1) the behavior is simple and consistent for everyone 2) a primary use-case is lowering priority of tools.
I also like that the result of that decision (layers decrease importance) acts to push against the common assumption that this will escalate things by making everything more and more important.
In any case, we need a good polyfill to offer people. OddBird is starting to look into this, but it's not very far along yet. Always open to contributions (or alternative approaches).
Sorry, the discussion here is a bit long and I didn't understand it.
Is it legal to use CSS-wide keywords in <layer-name>
or not?
e.g.
@import 'foo.css' layer(inherit);
@layer unset {}
@layer revert-layer {}
@yisibl I don't think that was part of this issue, but no – CSS-wide keywords are not allowed in layer names: https://drafts.csswg.org/css-cascade-5/#typedef-layer-name
Right, @FremyCompany's point is that this wouldn't, in any meaningful sense, "just work". You're still missing all your layered styles.
If unlayered rules came first instead of last, then I don't see why you couldn't just use old-fashioned methods to construct a basic reset+layout style that is just barely good enough, then (lazily :P) use the new hotness (layers) to do themes & everything else.
Sure, it would be highly unorthodox. But well within the realms of possibility (unless I am missing something).
Just as you could choose to define some fundamental/barebones functions in one JS file, then in another file using newer syntax, override those barebone functions and/or define "extra" functions that are only called if defined.
Granted, I can't think of a single reason why you'd ever want to do this with JS. But the CSS example is salient & tempting.
Should we maybe reopen this issue? Or should we open a new one?
From what I understand, the initial issue was the one preventing the browsers from shipping this as a feature. Now that the layers have shipped, it is obvious that a bunch of use cases are not covered by them.
Both the Candidate Recommendation and the latest Editor's Draft list this as an issue, highlighted in red and linked there. But the issue itself is closed.
The initial WG resolution is this:
RESOLVED: Reject this proposal; unlayered styles have a specified location in the layer stack which can't (currently) be controlled
Note the “(currently)”. I would interpret this as “ok for the first implementation, which we could return later to”. And this was actually the way I did interpret this in the past, and usually looking in the specs, seeing the still-present issue mentioned, I thought that this was a still-open issue.
I did bring this issue up recently on Mastodon, where it got some support.
The main use cases that interest me personally are style overrides for any sites or apps from outside:
User styles (extensions and Safari's ability to add a custom stylesheet) are often used by people to customize websites to their liking, often as a way for users to make sites accessible for them.
Two recent places where I saw custom style overrides mentioned in context of accessibility are this issue and this Mastodon reply by @simevidas (and the whole original thread by @delan).
Right now, anyone writing such overrides has to either fight with specificity, or push the !important
everywhere (or both, where the original pages contain heavy selectors with important declarations inside).
Custom CSS for various apps like Mastodon (usually available as a field for the instance admins) or Obsidian (ability to add just any CSS).
Apps like these do not come with all their styles neatly wrapped in layers, and for a long time, due to backwards compatibility issues, they won't. And many legacy similar apps never would (think of something like LiveJournal style overrides).
In all these cases, this case is similar to the first one: they need to either bump the specificity or use !important
.
One of the best things of CSS, its first letter — the Cascade — always had this intent of treating the users as those who should be able to write CSS as a way to customize websites. Not allowing the users to have a simple way to override things goes against this intent. While it can be argued that maybe browsers themselves should provide convenient ways of writing user origin styles, in reality they don't. Layers seem like the perfect place to unlock the overridability of the web, and make it more accessible for everyone.
I acknowledge that it is not as easy to decide how exactly the ability to add layers that go above unlayered styles should be defined, but I strongly think that we require this ability.
I don't think the problem here was a lack of use-cases, but the fact that we couldn't find a good path forward. I'd be happy to have this open again for more proposals, but I don't think it's a simple problem with a clear solution.
I'd also point out that user styles exist, and browsers have systematically made them harder to access. It sure seems like the problem of user-overrides should be solved in the 'origins' part of the cascade, rather than the 'layers' part? I don't mean to say that technical purity should block a more practical solution, if layers really are the best path forward - but it sure seems like we should look at the user origin first?
but it sure seems like we should look at the user origin first?
I don't know how CSSWG could impact this: to me this seems like a browsers' UI & UX issue in the first place. The ability to have user origin styles is already specified, the issue is that it is underused. And I'm not even sure if browser extensions can provide user origin styles, actually? I'm not knowledgable into how the whole extensions API is used, if it standartized and if it is even possible to require browser extensions to be able to provide user origin styles.
Also, all of this only covers one of the cases: the per-user in-browser overrides, while cases like “we'd want to provide styles for a mastodon instance as a whole as overrides, but not fight with the existing styles' specificity” won't be covered, as this is much closer to the layers idea.
I don't know how CSSWG could impact this
On the one hand CSSWG can't tell browsers what features to implement. On the other hand CSSWG is mostly browser vendors. Maybe more to the point, user styles are (currently) designed for a more 'preference'-like use-case, which browsers do support through limited GUI forms. To do real custom overrides from the user origin, you'd be back in the world of !important
. Or we'd need some other extension to user origins, which browsers and users find compelling. I imagine that may be why customization extensions don't bother with the user origin, though maybe they also lack access.
Also, all of this only covers one of the cases
Yep. Totally.
But to back up through some of the previous discussion here, and maybe reframe the feature request…
Assigning unlayered styles a universal-but-custom priority is what causes many of the issues, because 'unlayered' is a shared default. Usually, each stylesheet can namespace its own layers, and avoid conflicts when needed. But you can't name a layer that isn't there, and you shouldn't be moving around a layer that every other stylesheet treats as a default. Change its position for one stylesheet, and it breaks for others.
If every stylesheet has the same layer, it's the default layer, and anyone can move it around…? That gets pretty confusing and unreliable.
But the ability to layer a stylesheet is localized, it doesn't impact other unlayered styles, and it doesn't even impact the relation between layered and unlayered styles inside that stylesheet. Stylesheets remain internally consistent, even when layered on import. So it's already possible to position unlayered styles in the cascade layer order, right? The solution to unlayered styles might just be… layering those styles?
For the individual user cases, I think that should be possible for JS to do on the client side? Which an extension would be perfect for, since it can guarantee layer support.
And applications like mastodon and wordpress can start layering their own styles, if they want to expose customization features to their users? I hope they do! I know Wordpress has active discussions about it.
So then the question might be: can I as a masto admin override what Mastodon provides as part of their app? Which… I'm not sure if that makes sense as a CSS feature? Isn't that a feature request on the applications themselves?
# The solution to unlayered styles might just be… layering those styles?
Yes, but if you old browser doesn’t understand layers, you’re screwed and you end up with no styles at all.
# Can I as a masto admin override what Mastodon provides as part of their app? Which… I'm not sure if that makes sense as a CSS feature? Isn't that a feature request on the applications themselves?
Problem is when your application – be it Masto, WordPress, or whatever – does not use layers, you are prevented from using layers at all to add some extra overrides. You must use unlayered styles from that point on.
Personally, what I’d love to see is a simple way to say “this is a layer, but it wins from unlayered styles”. No need to reorder any existing ones or finely control where the unlayered styles should go.
In some thread about @scope
I floated around the idea of adding a !
to the at-rule itself, to indicate that it’s a strong one. For layers, this would be @layer!
. These would stack on top of unlayered styles, using the same layer logic for determining the order and what not.
E.g.
@layer! a, b, c;
@layer d, e, f;
@layer! g { … }
@layer h { … }
… would leave you with this order (ranked from high priority to low priority): g, c, b, a, unlayered, h, f, e, d.
Regarding CSS Cascade 5 (cascade layers), @jensimmons commented on another thread about layer ordering:
By default unlayered style come first (lowest cascade priority) in the source order, but this would allow more explicit placement. Roughly (pseudo-code):EDIT: That's no longer the case. In https://github.com/w3c/csswg-drafts/issues/6284 we reversed the behavior, and now unlayered styles have the highest priority. This explicit placement would still be useful, since there are use-cases for both approaches.
I think that feature makes a lot of sense, and I would likely use it as an author. A few considerations to keep in mind, as we develop a mechanism for this: