Is there a way to force a lower specificity in CSS?
Asked Answered
R

2

9

I have a CSS template where I want to use a minimal amount of attributes within some HTML markup, while allowing for easy customization of that markup via classes (not IDs) later, if needed.

<ul data-role = 'a'>
  <li>A</li>
  <li>B</li>
</ul>
<style>
  [data-role="a"] { /* ... */ }
  [data-role="a"] > :first-of-type { /* ... */ }
  [data-role="a"] > :last-of-type { /* ... */ }
</style>

The problem is that due to CSS specificity, I am faced with either making all of my selectors classes anyway, or else forcing any stylesheet modification of my content to be extremely specific:

<style>
[data-role="a"] > li { } /* custom overriding CSS */
</style>

vs

<ul class = 'a'>
  <li class = 'a-top'>A</li>
  <li class = 'a-bottom'>B</li>
</ul>
<style>
  a {/* */}
  a-top {/* */}
  a-bottom {/* */}
</style>

Is there a way to force a specificity yield without using !important, for example hypothetically:

@yield to classes {
  [data-role = "a"] { } 
  [data-role = "a"] > :first-of-type { }
  [data-role = "a"] > :last-of-type { }
  /* etc */
}

or

@specify('[data-role="a"] > :last-of-type' , 10) { ... } where 10 is the lowest internal specificity assigned, etc.

Or am I just forced to use classes everywhere?

Roughandready answered 21/9, 2019 at 7:7 Comment(3)
Being forced to use classes everywhere is the premise of many CSS frameworks, unfortunately. You do need a more specific selector if you want to avoid all that. You don't need an ID, but you do need just one more pseudo-class.Effector
I have a hard time visualising the problems you say you encounter. Do you want the later > li to override the earlier > :first-of-type? Then you can just write > li:nth-child(n) instead.Downing
In short, yes (it does already), but I want to use a class instead of some very specific attribute > element selector. I know how to increase specificity the regular ways.Roughandready
G
14

The MDN has an interesting tip to bail you out without using !important: Using a selector twice:

#myId#myId span { color: yellow; }
.myClass.myClass span { color: orange; }

https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity

Recently support for the :where() pseudo class was added to the major browsers. It allows managing specificity better. The selectors within do not contribute to the specificity of the overall selector:

:where(aside.where, footer.where) a {
  color: orange;
}

footer a {
  color: blue;
}
<aside class="where">
  <p>Here is my aside content. This <a href="https://developer.mozilla.org">also contains a link</a>.
</aside>

<footer class="where">
  <p>This is my footer, also containing <a href="https://github.com/mdn">a link</a>.
</footer>

https://developer.mozilla.org/en-US/docs/Web/CSS/:where

Further note that according to an approach called BEM (Block, Element, Modifier) using classes (one class for any element) might not be the worst choice anyway.

Ginoginsberg answered 9/4, 2021 at 15:24 Comment(1)
:where() is exactly what I was looking for. It brings everything inside it to specificity 0. This makes it perfect for overriding browser defaults or gradually lowering the specificity to the desired level.Westfall
S
0

Is there a way to force a specificity yield without using !important? ... or @specify('[data-role="a"] > :last-of-type' , 10) { ... } where 10 is the lowest internal specificity assigned

No, there is not.

Not sure what else you are looking for out of an answer since you seem to understand how specificity works in general -- with just the small HTML you provided there at least dozens of ways you could write rules involving elements, classes, attributes, etc. to get your desired styling. Though if you're looking for a simple, well-written write-up on specificity I've always found this one to be the most useful:

https://css-tricks.com/specifics-on-css-specificity/ (continually updated since 2010!)

Sisneros answered 2/10, 2019 at 19:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.