Proposal: update `:where(:root, {component}), ` -> ~~`:where(:root), [data-reset={component}]`~~ `:where(:root), pf-{component}-reset]`
TLDR:
:where(:root), :where(.selector) is a redundancy and produces the same result as :root, .selector. We created this model to address scoping issues. For example, page defines menu-toggle's background-color as blue, but card (within page) requires menu-toggle to use default colors. You would then have to redefine the menu-toggle color in card. The purpose of :where(:root), :where(.selector) is to prevent this scoping issue, but unfortunately it presents a new issue. It prevents page from defining menu-toggle background color entirely without increasing specificity.
This results in having to write something like:
.page {
.menu-toggle {
--menu-toggle--BackgroundColor: blue;
}
}
Instead of:
.page {
--menu-toggle--BackgroundColor: blue;
}
Additionally, it means that users can't have a flat theming file:
Instead of this:
.my-custom-theme {
--menu-toggle--BackgroundColor: blue;
}
Users must also increase specificity
.my-custom-theme {
.menu-toggle {
--menu-toggle--BackgroundColor: blue;
}
}
The solution?
- Components need a reset that is independent of their base class.
- Both
:rootandresetrequire specificity of 10
Possible names
:where(:root), [data-reset="selector"]@mcoker @srambach update:root, data-reset-menu-toggle {}- `:root, [data-reset-style="menu-toggle"] {}
Additional benefits of this method
- Flexibility: Using this pattern
[data-reset-style="component"]is ubiquitous. It could be included in any component, not just the specific component. So,.carditself can resetmenu-toggleor any other component for example. - Nestability:
.pagecan define.menu-toggle,.page-sectioncan reset.menu-toggle,.cardcan reset.menu-toggleto its default state.
Example
CSS
.page {
--menu-toggle--BackgroundColor: blue;
}
.page-section {
--menu-toggle--BackgroundColor: red;
}
HTML
<div class="page">
<button class="menu-toggle"></button> // color blue
<div class="page-section">
<button class="menu-toggle"></button> // color red
<div class="card" data-reset="menu-toggle">
<button class="menu-toggle"></button> // reset to default state
// here menu-toggle can be redefined by parent
// here menu-toggle reset to default
</div>
</div>
</div>
My custom theme
.my-theme {
--menu-toggle--BackgroundColor: fuscia; // this can then be reset by [data-reset="menu-toggle"]
}
Process and approach
- Replace
:where(:root), :where(.component) {}in a 3-5 components of varying complexity. - Run visual regression tests
- Analyze results and triage feasibility
Additional thoughts
I haven't tested this, but we may be able to do something like this with card CSS
:root,
[data-reset="card"],
[data-reset="menu-toggle"] {
}
Notes from 5/14 discussion
@mcoker @srambach
Q: Why should we remove :where()
A: It may be an anti-pattern, but we will look into this. The more important aspect of this proposal is the .selector prevents the cascade from behaving as expected. This approach to scoping prevents parent elements from styling child component without increasing specificity.
Notes from 5/15 community meeting
Useful links
Issues Cascade layers: (proposal PR) Root scoping: (proposal PR)
Resources
- https://devtoolstips.org/tips/en/find-expensive-selectors/
- https://csswizardry.com/2011/09/writing-efficient-css-selectors/
- https://blogs.windows.com/msedgedev/2023/01/17/the-truth-about-css-selector-performance/
Potential issues
- Should make sure it supports Firefox enterprise service release (no easy-reference support notes, need to find specific version ESR points to)
- Chrome and Edge CSS selector performance upcoming (slack convo)
- Using an attribute selector
[data-reset="selector"]may be expensive. Explore this.
Possible alternative
- use a class instead
.pf-v6-card-reset
I wonder if something like [data-reset="selector"].pf-v6-reset could work? Going from right to left with bottom-up parsing (like CSS selector matching does), it'd look for everything with the class first, then do the more expensive selecting on that subset, instead of trying to do an attribute selector across the entire DOM.
The side effect is that you'd need to specify it in two places instead. I wonder if there could be a more general reset and then you could specify? That could probably be targeted like this then, if so?
:not([data-reset]).pf-v6-reset { /* reset all here */ }
...but this is all off the top of my head and not tested, after a day full of meetings. :wink:
But I'm not sure if this matters or not, as the right-to-left thing might be just for combinations making up a selector and not necessarily compound selectors like this. (I'm not sure if this would be considered a combination or compound, however.)
Here's another blog post about the CSS selector matching functionality that's in Edge (and soon in Chrome), from the Edge dev team, which walks through how to use it with some examples too:
https://blogs.windows.com/msedgedev/2023/01/17/the-truth-about-css-selector-performance/
I wonder if something like [data-reset="selector"].pf-v6-reset could work? Going from right to left with bottom-up parsing (like CSS selector matching does), it'd look for everything with the class first, then do the more expensive selecting on that subset, instead of trying to do an attribute selector across the entire DOM.
@garrett I'm always a proponent of simplicity. Seems like .pf-v6-c-{component}-reset (.pf-v6-c-card-reset, .pf-v6-c-description-list-reset, .pf-v6-c-button-reset) would work well. WDYT?
Sure! A simple class to match is the second fastest selector possible. (Fastest if you don't count #IDs, which really shouldn't be used in CSS, if you can help it, so that'd be perfect for performance and good for simplicity too.)
This issue has been automatically marked as stale because it has not had activity in the last 60 days.