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-specificity] Add specificity property modifier (replace !important) #3890

Closed
BennyHinrichs opened this issue May 3, 2019 · 9 comments
Closed

Comments

@BennyHinrichs
Copy link

BennyHinrichs commented May 3, 2019

Currently there is the ability to hoist a property's specificity beyond that of its selector via !important. This often leads to people writing labyrinthine selectors, then slapping !important on the properties to override a previous developer's styles.

What if where you put !important, you could put !specificity(10)? What if in developer tools, every rule/property showed its specificity in number form? This would effectively change the esoteric nature of specificity to something much easier to determine and comprehend. It would be very clear to see, "Oh, my rule isn't being applied because it's of specificity 2 and there's another one of specificity 5."

Let's say that you want to ensure your .hidden class always hides the element it's applied to. In your stylesheet you could have something like:

:root {
  --sp-low: 1;
  --sp-med: 10;
  --sp-high: 20;
}

.hidden {
  display: none !specificity(var(--sp-high));
}

Or imagine switching between light and dark modes. All you would have to do is change the specificity variable of the dark to be greater or less than that of the light.

:root {
  --sp-light: 1;
  --sp-dark: 0;
}
document.getElementById('themeToggle').addEventListener('change', ({target}) => {
  document.documentElement.style.setProperty('--sp-dark', target.checked ? 2 : 0);
}

Or say you want to set the specificity for an entire selector:

.nav {
  all: !specificity(2);
}

It would make specificity akin to how z-index is now. If you can't see your element, it's pretty straightforward to compare z-indices. One potential issue is that it opens specificity up to abuse (e.g. !specificity(100000000). That being said, I think it's mild compared to the abuse it currently suffers. I personally prefer a straight number over trying to parse my way through selector chains and !importants.

EDIT I know that there are 5 levels of specificity. This proposal is specifically being able to assign a number to !important.

@AmeliaBR
Copy link
Contributor

AmeliaBR commented May 4, 2019

I think the overall idea is intriguing. But using CSS variables inside an importance modifier wouldn't work — you need to sort out specificity & the cascade earlier in the process than variable substitution.

@SelenIT
Copy link
Collaborator

SelenIT commented May 4, 2019

It's worth noting that we already have a specificity adjustment mechanism, and there was a proposal to extend it with an optional parameter for explicit specificity setting. However, the specificity itself is only the 3rd styles priority criterion in the cascade, following origin/importance and scope, so even the hypothetical !specificity(Infinity) modifier would not override the animation styles defined in the same CSS file (as well as !important declarations from the same file!). That's why I'd say that the idea of creating custom cascading levels (or "origins") would be more interesting to explore.

@SebastianZ
Copy link
Contributor

What if in developer tools, every rule/property showed its specificity in number form?

That's a very good idea, though it's independent of your proposal for allowing to modify the specificity. For reference, there exists already a request for this for the Firefox DevTools.

Sebastian

@dvoytenko
Copy link

dvoytenko commented Apr 8, 2020

The proposal looks good to me. Especially if the argument is a constant number and not a var. However, the issue with this proposal is that is not polyfillable and thus it'd take quite a long time before it can be used freely across the Web. Currently the main tool available to us is "keep CSS selectors as simple as possible and order CSS rules correctly". In a naive case, if all CSS selectors were a single class (.myclass) selectors, then the best specificity control is the order of CSS rules. So I'm wondering if it'd be possible to construct this proposal to ensure that it can be fully pollyfillable using a simple and safe reordering. If it were possible, then PostCSS/WebPack/etc plugins could close the gap.

For instance, the following constraint could help: the specificity argument cannot be smaller than the CSS selector where it's used. Or (better?), the argument would mean "added specificity" and will be added to the selector's specificity. To stress: I'd certainly like the ability to lower specificity in this proposal, but it cannot be polyfilled.

@chrishtr
Copy link
Contributor

chrishtr commented Apr 9, 2020

I think polyfillability of this feature is important. It should be defined in a way that toolchains
for building sites can take advantage of it in advance of full browser support.

Maybe we could support specificity on a per-stylesheet basis rather than per-selector?

@dvoytenko
Copy link

@chrishtr I actually think if the !specificity() value is properly constraint, per-property declaration can still be polyfillable. So, if it's a more promising declaration style, it's worth trying to make it work.

@BennyHinrichs
Copy link
Author

BennyHinrichs commented Apr 9, 2020

@dvoytenko Just to clarify, specificity of properties is a 5-dimensional value: [important, inline, id, class, tag] where any value to the left supersedes any value to the right. See https://cssspecificity.com/.

If I have an inline rule of style="background:black!important" that gives me a specificity of [1, 1, 0, 0, 0]. This supersedes a stylesheet rule of .container{background:black!important}, which has specificity of [1, 0, 0, 1, 0].

My proposal—and of course it could be altered—is to give a way to freely manipulate the first member of the specificity set. Right now it has binary functionality, with only 0 and 1 as possibilities. If you want to override an !important, you currently have to do it by increasing selector complexity. If the !important is in an inline style, increasing selector complexity won't work. You have to manipulate the inline style. If you don't have source access, you have to use JavaScript to manipulate that node, and make sure you don't accidentally erase other inline styles in the process.

I used the word specificity in the OP, but perhaps a better way to imagine it would be if it just used important. Then our rule above could be .container{background:black!important(2)}, and that rule would win over the inline rule with specificity of [2, 0, 0, 1, 0].

As for backward-compatibility, people could potentially be encouraged to use @supports:

@supports (important(2)) {
  .container {
    background: black !important(2);
  }
}

@SelenIT
Copy link
Collaborator

SelenIT commented Apr 9, 2020

specificity of properties is a 5-dimensional value

Not exactly. It's often taught to the beginners that way for simplicity, but the CSS Cascade spec defines the "Origin and Importance" and "Specificity" as two separate criteria of the declaration precedence. Adding "!important" effectively changes the first one, but even !important author styles still can't override !important user agent styles, and transition styles override everything (so setting a transition with a huge delay would effectively make any CSS value unchangeable).

So adding only extra importance levels without adding new "origins" would not allow, e.g., making a declaration's priority higher than any non-important author style (to prevent accidental overriding) but lower than animation declarations (to keep them animatable).

@tabatkins
Copy link
Member

For general adjustment of a selector's specificity, that's the planned additional arguments to :where().

!important isn't technically part of specificity; it controls a declaration's cascade origin. Custom Cascade Origins are the current plan to allow adjusting that aspect; the WG accepted work on it at the last f2f, and we should have a good spec out of it soon, once we resolve a few issues.

I'll go ahead and close this issue, then, since #4470 now covers the topic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants