Skip to content
Please note that GitHub no longer supports Internet Explorer.

We recommend upgrading to the latest Microsoft Edge, Google Chrome, or Firefox.

Learn more
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] Custom cascade origins #4470

Open
mirisuzanne opened this issue Oct 29, 2019 · 8 comments
Open

[css-cascade] Custom cascade origins #4470

mirisuzanne opened this issue Oct 29, 2019 · 8 comments
Labels

Comments

@mirisuzanne
Copy link

@mirisuzanne mirisuzanne commented Oct 29, 2019

This relates to the Cascade Specification, along with a number of "specificity" concerns and proposals (such as #2272 & #3890 & the :where() selector).

Much of my work with design systems has revolved around helping companies define layers of abstraction: building tokens, then defaults, then patterns, components etc. That's a common approach, whether we call it OOCSS or Atomic Design or ITCSS or something else. In order to do that, we often have to be very careful with matching specificity to layer – so components override patterns, and so on – and third-party tools can easily break a delicate balance.

It strikes me that cascading origins & !important are designed to solve that same problem on a larger scale (UA, user, author), and then reverse-order for !important styles. It's a pretty clever solution, but !important is a blunt instrument for handling layers inside the author origin.

I doubt most developers think about cascading origins, or the role importance plays in it - and at this point they don't really need to for practical reasons. I don't have a full solution here, but a rough sense that providing control of custom cascade origins (within/around the author origin) might help:

  • provide a useful tool for solving many issues seen as "a specificity problem"
  • help teach the powerful concepts already built into the core of the language
  • make it more clear how CSS and !important are designed, and how they work under the hood

A few notes on finding a syntax/approach that would work:

  • The property-by-property !important approach (or !default proposal which I like) is useful in other situations, but too narrowly applied for this particular use-case
  • The selector-specificity :where() approach is both narrowly-applied and removes all specificity, which could still be a useful tool within origins
  • I imagine something more similar to a media-query, set in either @import or an at-rule of some kind (e.g. @origin?). That feels like the more proper scope for this problem.

I realize this gets difficult to define quickly:

  • Are origins generated with numbers or names? Names feel useful, but would have to be ordered somewhere, and that makes for another layer of complexity.
  • What happens when a third-party library contains @origin blocks internally, and I want to load it in a specific layer within my overall code. Are there layering contexts, similar to z-index? Nesting origins would get complicated quickly.

I hope that all makes some sense. I'd be curious for additional thoughts on this, and happy to clarify anywhere I can.

@mirisuzanne

This comment has been minimized.

Copy link
Author

@mirisuzanne mirisuzanne commented Oct 31, 2019

It strikes me that in some ways specificity already works in isolated layers:

  • One class/attribute overrides any number of elements
  • One ID overrides any number of classes and attributes

Clearly that was the original intent: components (IDs) override patterns (classes/attributes) override defaults (elements). But it maybe falls apart in modern use for a few reasons:

  • IDs are explicitly one-off, so we can't re-use anything in the component layer
  • Elements are too broadly scoped for most patterns, since HTML has a limited set built-in
  • That leaves us mostly limited to the middle layer of specificity (classes and attributes) which can handle any range of broad-to-narrow styling

That could lead us down a path of providing more specificity layers rather than origin layers? I'm not sure…

@LeaVerou

This comment has been minimized.

Copy link
Contributor

@LeaVerou LeaVerou commented Jan 22, 2020

The selector-specificity :where() approach is both narrowly-applied and removes all specificity, which could still be a useful tool within origins

Its goal is exactly the opposite: because it's a pseudo-class, it allows only removing part of the selector from the specificity calculation, enabling authors to distinguish importance-signifying criteria from merely filtering criteria. Of course it can be used for the whole selector, but it doesn't have to be, nor was that the primary use case I had in mind when I proposed it.

@css-meeting-bot

This comment has been minimized.

Copy link
Member

@css-meeting-bot css-meeting-bot commented Jan 22, 2020

The CSS Working Group just discussed Custom Origins.

The full IRC log of that discussion <fantasai> Topic: Custom Origins
<jensimmons> https://noti.st/jensimmons/QOEOYT/three-topics#srFUYHC
<astearns> github: https://github.com//issues/4470
<TabAtkins> jensimmons: Possibility of custom cascade origins, controlled by authors.
<TabAtkins> jensimmons: Part of a larger convo, which could be called "modernize the cascade"
<TabAtkins> jensimmons: Why modernize? Some folks argue that specificity was designed for a simpler time, when one or a small number of people wrote the CSS for a site. Today CSS is maintained over years by large teams, and the cascade gets really hard.
<TabAtkins> jensimmons: If a dev gets a ticket, they can't really rearchitect the whole page's cascade to fix that one thing.
<TabAtkins> jensimmons: Lots of ways people attack this.
<TabAtkins> jensimmons: (1) just do it right the first time
<TabAtkins> jensimmons: (2) OOCSS, SMACSS, BEM, etc
<TabAtkins> jensimmons: (3) Only ever use one class, to give identical specificity and remove the cascade.
<TabAtkins> jensimmons: (4) overuse !important
<TabAtkins> jensimmons: (5) CSS-in-JS, ignoring cascade again
<TabAtkins> jensimmons: Problem there is no way to control order CSS is loaded. No wonder the cascade is confusing!
<TabAtkins> jensimmons: (6) just inline-style everything, screw selectors
<TabAtkins> jensimmons: Why not use specificiy as designed?
<TabAtkins> jensimmons: IDs increase specificity, but can only use it once per page
<TabAtkins> jensimmons: Not great for components.
<TabAtkins> jensimmons: Element selectors work well for simple defaults, but too dependent on doc structure, and hard to use otherwise.
<TabAtkins> jensimmons: So leaves a lot of these teams only using classes, attributes, and !important
<TabAtkins> jensimmons: [shows example]
<TabAtkins> jensimmons: [Tailwind CSS]
<TabAtkins> jensimmons: [everything's inline, with no cascade]
<TabAtkins> jensimmons: A lot of possible ideas here too, web components, scoping, etc.
<TabAtkins> jensimmons: A project I did last year is how to protect CSS from this hate.
<TabAtkins> jensimmons: So we put together a hard-core course on teaching the cascade.
<TabAtkins> jensimmons: Miri Suzanne did a deep dive into the history/etc.
<TabAtkins> jensimmons: She began thinking about how we could change CSS to modernize the cascade and work better.
<TabAtkins> jensimmons: One of her ideas is to extend selectors, in #4690.
<astearns> https://github.com//issues/4690
<TabAtkins> jensimmons: Another idea is to allow authors to make custom cascade origins.
<TabAtkins> jensimmons: I didn't really know what a cascade origin was until Miri taught me.
<TabAtkins> jensimmons: [describes the cascade origins]
<fantasai> See https://www.w3.org/TR/css-cascade-4/#cascade-origin
<TabAtkins> jensimmons: Proposal is for custom origins. Say, create 3 named origins (get !important variants automatically that work as expected), and put styles in the chosen origin to get auto-overriding.
<TabAtkins> jensimmons: So use case.
<TabAtkins> jensimmons: Reset styles in one origin, design system in another, then one-off overrides into a third.
<TabAtkins> jensimmons: Or split apart the design system: reset -> defaults -> patterns > layouts -> components, all distinct origins.
<emilio> q+
<TabAtkins> jensimmons: Or CMS Core -> CMS Extensions -> Base theme -> site styles
<TabAtkins> jensimmons: Or a team trying to rewrite their CSS. Can't fix it all at once, but could put all their old code in one origin, and put their new code in a higher origin, to piecemeal fix it as they go.
<TabAtkins> jensimmons: Or Bootstrap -> 3rd party -> ad networks -> actual site styles
<TabAtkins> jensimmons: Adventages?
<TabAtkins> jensimmons: Coudl help with specificity wars between frameworks and author styles.
<TabAtkins> jensimmons: Could put !important back into its proper role, rather than being abused just to get a second origin.
<TabAtkins> jensimmons: Or just using origins as a type of !important; might be just as annoying?
<TabAtkins> jensimmons: Pulled some use-cases from Twitter (already mentioned)
<TabAtkins> jensimmons: So what do you think? Want to pursue?
<hober> q?
<florian> q+
<fremy> q+
<astearns> ack emilio
<fremy> q-
<TabAtkins> emilio: I'm a bit confused abuot !important.
<TabAtkins> emilio: If you want ad networks on an origin, and your styles on a higher origin, the ad networks could still override everything with !important style. Maybe that's not what you want?
<bkardell_> q+
<TabAtkins> emilio: Second, it may be invalid, but IDs *can* be repeated on the page...
<TabAtkins> emilio: There are ways for authors to use cascading origins that have better behavior - web components.
<fantasai> fantasai: They're really hard to use
<fantasai> TabAtkins: And also won't handle these use cases
<TabAtkins> TabAtkins: WC doesn't solve any 0f Jen's use-cases, tho.
<TabAtkins> emilio: When we discussed custom element default styles behavior, Apple was strongly against. Unsure if the'd still have complaints.
<fantasai> i/emilio/iank_: We should add declarative shadow roots
<TabAtkins> hober: I'l talk to Ryosuke/Antii, see if they have feelings on this.
<emilio> Though ++ to declarative shadow roots
<astearns> ack florian
<TabAtkins> florian: I think it's a brilliant idea.
<TabAtkins> florian: We've had the luxury of multiple origins here in CSS, letting us design thru problems. Authors haven't had that.
<TabAtkins> florian: I think it would be great. Almost want to stop talking about whether or not to do it and jus tstart talking syntax.
<TabAtkins> florian: Even as a singl eauthoe this seems useful.
<TabAtkins> q+
<astearns> ack fantasai
<TabAtkins> fantasai: I always want to say I love it.
<astearns> ack dbaron
<TabAtkins> dbaron: I'm also a big fan.
<TabAtkins> dbaron: There are multiple choices we coudl make about !important.
<TabAtkins> dbaron: Don't have to say they go in the opposite order. They could go in the same order, or be configurable, etc.
<fantasai> +1
<TabAtkins> dbaron: Maybe have the !important right after the normal origin.
<fantasai> essentially an origin can encapsulate its !important level
<TabAtkins> dbaron: So lots of options we could choose between, or let authors configure.
<AmeliaBR> +1 to dbaron says. Definitely don't want !important to automatically do reverse order.
<TabAtkins> fantasai: Along those lines, might have an origin with sub-origins.
<heycam> q+
<TabAtkins> fantasai: Which might have its !important held within the larger origin
<astearns> ack bkardell_
<TabAtkins> bkardell_: First, thanks for bringing it up.
<astearns> q+
<TabAtkins> bkardell_: I've had these same conversations and I think this is really healthy.
<fantasai> Examples (from slide 25):
<fantasai> Reset < Design System < Overrides
<fantasai> Reset < Defaults < Patterns < Layouts < Components
<fantasai> CMS Core < CMS Extend < Base Theme < Site Styles
<fantasai> Old Styles < New Styles
<astearns> q-
<fantasai> Bootstrap < 3rd-party libs < Ad network < Site Styles
<TabAtkins> bkardell_: To discuss what people are actually doing, rather than just relying on education
<TabAtkins> bkardell_: I think CSS-in-JS does have an order...
<TabAtkins> jensimmons: They can, but don't always
<TabAtkins> bkardell_: wrt WC, they don't solve all problems, but they do solve some. They're already .2% of the web archive, despite only getting the last impl this week.
<TabAtkins> bkardell_: Do we really rely on origin for UA level? I thought we kept them low speicficity.
<TabAtkins> TabAtkins: We don't use IDs, no, but we do freey use attribute selectors, which can easily clash if it wasn't for the origin difference.
<jensimmons> slides (again): https://noti.st/jensimmons/QOEOYT/three-topics#srFUYHC
<fantasai> yes, we really rely on origin for UA level
<TabAtkins> bkardell_: I do believe we'r emissing something here. I don't know if this addresses or exacerbates the problem. At some level it addresses their complaints, but by doubling down on what they're complaining about.
<TabAtkins> jensimmons: Agree.
<astearns> ack TabAtkins
<fantasai> Scribnick: fantasai
<fantasai> TabAtkins: I totally like this idea
<fantasai> TabAtkins: had similar thoughts, but never did the use case exploration
<fantasai> TabAtkins: Definitely agree we should pursue this, and the use cases made me absolutely sure we should pursue this
<TabAtkins> ScribeNick: TabAtkins
<astearns> ack heycam
<TabAtkins> heycam: I think it's very important fo rus to try to address these problems.
<TabAtkins> heycam: A little of a shame that it's taken several years after people started complaining, biut glad we're addressing it now.
<TabAtkins> heycam: What I like about this is that it's so simple, and slots into the existing model.
<astearns> q+
<TabAtkins> heycam: Not super sure about whether it really will capture all of these use-cases, or might need more discussion with real proponents of CSS-in-JS to see how well it works.
<emilio> q+
<TabAtkins> heycam: I'd want to be more sure this is the right way to go for solving that.
<TabAtkins> heycam: But see th eother use-cases anyway.
<TabAtkins> astearns: I agree this is very nice it slots into our model, but a little concerned it's not the general author model.
<TabAtkins> astearns: You had to learn about the concept anyway.
<TabAtkins> astearns: So as Tess said, "origin" is an overloaded term, maybe we can come up with something else?
<TabAtkins> [various suggestions]
<astearns> ack astearns
<astearns> ack fantasai
<dbaron> "style sources"?
<TabAtkins> fantasai: Some discussion about this addressing all the cases; I don't think it does, biut it addresses quite a few, and addresses the organizational layer of many projects.
<TabAtkins> fantasai: So I think it fits well with how people put together their sites.
<TabAtkins> fantasai: There's other places in the cascade where specificity gets unwieldy. I don't think WC is great here; it adds a *ton* of encapsulation.
<TabAtkins> fantasai: Another proposal was scoped styles in CSS, which might also help.
<jensimmons> q?
<bkardell_> if we had this, would we need leaverou 's zero specificity pseudo still too?
<TabAtkins> fantasai: They let you say "within this sidebar, these styles win over other things".
<fantasai> s/things/things, regardless of specificity/
<TabAtkins> TabAtkins: I think declarative shadow dom addresses a lot of the problems with WC; I'd like to explore that more seriously first before we add something that is 98% identical to WC's model, but with 2% weird differences that make impl complicated.
<astearns> ack emilio
<fantasai> bkardell, you wouldn't need it for an entirely swath of styles, but would likely still be useful locally, for specific selectors or parts of selectors
<AmeliaBR> q+
<TabAtkins> emilio: I agree this is neat. Is there a concrete proposal? Is that at the stylesheet level, or allow 3rd party styles to choose their origin, etc?
<TabAtkins> emilio: Depending on details it might solve some use-cases but not vice-versa.
<TabAtkins> emilio: Also need to figure out how this interacts with shadow dom.
<leaverou> bkardell_: I believe so. This is great, but sometimes you need more fine-grained control. E.g. when theming *within* the same origin
<TabAtkins> emilio: Shadow DOM introduces a stack of origins; introducing this naively makes it a matrix, which is harder.
<jensimmons> q?
<astearns> ack AmeliaBR
<TabAtkins> AmeliaBR: echo Emilio's concern that we need details to see exactly how this sort of thing works.
<TabAtkins> AmeliaBR: Coming up with specific proposals and cross-reffing them with specific use-cases would be helpful.
<TabAtkins> AmeliaBR: So we should work from the use-cases to what features we actually need.
<astearns> ack fantasai
<TabAtkins> fantasai: For "how do you control", an easy way to think of it would be the person importing the sytlesheet bea ble to say what level it imports at.
<TabAtkins> fantasai: And within each level, maybe you can have sub-ordering.
<florian> q+
<TabAtkins> fantasai: And with a nesting block that lets you specify the layer within a single file.
<TabAtkins> fantasai: Using numbers to establish the ordering might work if there's only one controller; multiple controllers gives you the z-index wars.
<faceless2> q+
<TabAtkins> emilio: My concern with nubmers or letting stylesheets choose their own levels becomes a z-index fight.
<astearns> ack florian
<TabAtkins> florian: One thing I'm a little concerned is how we figure out the syntax to have a migration path toward this from legacy CSS.s
<TabAtkins> florian: In particular, a syntax ignorable by old browsers is bad because the cascade will be all mushed up; but making it hide rules from old browsers means they'll just miss a lot of code.
<TabAtkins> florian: Writing everything twice is bad, but not having an upgrade path is bad.
<astearns> ack faceless2
<astearns> ack faceless
<TabAtkins> faceless2: What if you had two toolkits, importing the same stylesheet at different levels?
<astearns> zakim, close queue
<Zakim> ok, astearns, the speaker queue is closed
<fantasai> TabAtkins: Same as importing a style sheet twice, it's just present in both places
<fantasai> TabAtkins: cascades together; effectively later one wins
<TabAtkins> jensimmons: So got a lot of good issues and concerns.
<bkardell_> +1 to talk about "this set of problems" for sure
<TabAtkins> jensimmons: I do think it's worth looking deeply at the solutions we might need for the complete set of problems, not just what this particular solution could address, so we can tell if this is a good idea in the totality of a complete solution.
<TabAtkins> jensimmons: I've even convinced myself that if we ship this today by itself, it could get abused pretty badly.
<TabAtkins> jensimmons: (similar to people abusing Flexbox to do grids)
<TabAtkins> jensimmons: Putting this on Twitter, I got a lot of trepidation from folks. Powerful tool, could be bad.
<TabAtkins> jensimmons: But I got that people who really knew CSS the most thought this was a terrific idea.
<TabAtkins> jensimmons: I think it does require some teaching, but it's not that complicated.
<TabAtkins> jensimmons: So I'm hearing a tentative "yeah, this is good", but I think there is a bigger metaproject to modernize the cascade.
<TabAtkins> jensimmons: Also, Miri has been very active in Sass to push CSS to be a featureful language; did crazy things with Sass variables back in the day.
<TabAtkins> jensimmons: So I'd like to invite her as an IE.
<TabAtkins> [intentionally not minuting]
<TabAtkins> fantasai: Where to put it?
<TabAtkins> TabAtkins: Suggest putting it in WICG until it gels, then merge it into Cascade 5.
<fantasai> i/fantasai/astearns: So sounds like interest in the room, try to move proposal forward/
<TabAtkins> jensimmons: And I want to get some highly-skilled authors involved in the convo too, so hopefully WICG works there.
@astearns astearns removed the Agenda+ F2F label Jan 24, 2020
@WebMechanic

This comment has been minimized.

Copy link

@WebMechanic WebMechanic commented Jan 25, 2020

came here via one of Jen's Tweet. https://twitter.com/jensimmons/status/1219351448028356609

* IDs are explicitly one-off, so we can't re-use anything in the component layer

well, actually CSS doesn't bother. You can have multiple elements with the same ID and they'd be styled according to the current rules.
It's JS that'd likely choke or sth. like a XSLT processor.

Maybe I got lost in translation reading this proposal (and what happend around Jen's tweet) and I'm going to state the obvious; but here are my 2ct.

FWIW both Cascade and Specificity are and always have been perfectly fine the way they are: the closer to the element (origin/location/cascase) considering the selector specificity. Done.
What's not to grasp? :)
It's kinda like with using Layers and Masks in Photoshop: some ppl just can't get their head around it :)
If you one doesn't understand pointers in C++ or references iin Java[Script] s/he equally screwed.

However, the culprits IMHO happen to be "modern" design patterns and philosophies like BEM or Atomic Design & such that are causing the issues ppl. apparently have with "specificity": you either have a rather flat hierachiy where everything has virtually the same specificity (i.e. BEM) (and origin/order matters) or the HTML is poluted with a gazillion cryptic utility classes and what-not that might end up raising the specificity over the roof. And then there are some Framework / Library creators that don't get it either and butcher our beautiful Cascade adding !important all over the place.

There's only one addition to the Level in the Origin line of things I'd imagine to be helpful in some situations and to some people. It'd be considerably 1. logical and 2. following the current rules of the Cascade but applied a different "weight" to stylesheets served from 3rd party domains (CDN et al) and one's own (sub-)domain.
That's something crafty people can easily understand and hopefully handle responsibly.
That's also how CDN based CSS Frameworks would get a lower significance than author and user styles.
Currently only source order matters, IIRC.

With such a distinction equal selectors and equally specific selectors in an author style (even if @imported) served from the same domain/origin as the document could overrule one from a CDN'ed Framework w/o using !important.

Here's how things could work bold = "new" origin level (out of my head from lower to higher priority)

  • UserAgent
  • link with @import 3rd party domain (external origin)
  • link 3rd party domain (external origin)
  • link with @import to author styles (same/local origin)
  • link to author styles (same/local origin)
  • document level <style>@import
  • document level <style>
  • element level (inline) style attribute / JS
  • user styles
    Order of appearance in each "level" applies as usual; that is the closer the more significant.
    Somewhere in each level would be animation/transition and the daddy of all Evil !important.

Since this additional level of origin would change the current browser behaviour, authors should "opt-in" to enable the new origin level by adding a <meta> tag to the document or sending a response Header.
That's similar to the well accepted <meta viewport> which also changes the way CSS works with dimensions, or how oldIE was told to toggle its X-UA-Compatible mode back in the days.

René

@keithjgrant

This comment has been minimized.

Copy link
Contributor

@keithjgrant keithjgrant commented Jan 31, 2020

I am very excited by this proposal, and have definitely wished for this exact thing. Even if authors can't define n origins, just two would go a long way: One for "base" styles, and another for "module" styles.

I think in most modern development, there are fundamental differences between these two types of styling. Ideally, I would typically want base styles to apply to the whole page, even piercing into Shadow DOM; but they would be of a lower priority origin, so module styles would always override them. I'd even be interested in a way to do something like all: unset that reverts my module styles but leaves my base styles applied. Related comment: #3547 (comment)

@keithjgrant

This comment has been minimized.

Copy link
Contributor

@keithjgrant keithjgrant commented Jan 31, 2020

applied a different "weight" to stylesheets served from 3rd party domains (CDN et al) and one's own (sub-)domain.

I think this might be useful to some, but it only addresses one specific problem that's a subset of this problem as a whole. Not everyone pulls in (say) Bootstrap from a CDN and then sticks some overrides on it. How would this help me override my own base styles? For me, I need more control over my styles and how they are applied.

@tabatkins

This comment has been minimized.

Copy link
Member

@tabatkins tabatkins commented Jan 31, 2020

Having N author-defined origins is realistically no more difficult than having two (in other words, the spec text and probably implementation for both would be nearly identical, just with one being locked into 2), so that's nice at least.

@WebMechanic

This comment has been minimized.

Copy link

@WebMechanic WebMechanic commented Feb 1, 2020

Not everyone pulls in (say) Bootstrap from a CDN and then sticks some overrides on it.

by CDN I simply mean "not the same hostname".

I often set up one or more subdomains on the very same server pointing to the same files to overcome the connection limits (esp. for HTTP/1.1). So cdn.myserver.tld or img.myserver.tld and myserver.tld are actually the same machine and share the same root. Some trickery in Apache's .htaccess makes sure that only certain file types are delivered by and only accessible via the CDN URLs.

There you have you own "CDN" to stick some overrides on it :)
Same simple rules apply.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
8 participants
You can’t perform that action at this time.