Stylelint: disable certain rules? Issue with no-descending-specificity

Hello.

Is it possible to disable certain eslint rules?
Wasn’t able to find anything specific inside resources/assets/build/ that might help with this.

For example, I’m having many issues with this specific rule: ‘no-descending-specificity’; it throws errors like:

Expected selector ".card.card-simple:hover .card-footer button" to come before selector ".img-split .img-split-el .img-split-cont:hover      no-descending-specificity
           .img-split-btn button"

This prevents me from continuing my work or just forces me to do things very differently which is not alway good. Any ideas about how to get around this rule, that I believe is a bit broken?

Thanks!

Solved it by setting this rule to null in package.json:

"rules": {
      "no-descending-specificity": null,

If anyone has a solution for why the rule is triggered, would be better to leave it turned on of course :slight_smile:

2 Likes

It’s being inherited by the preset.

https://github.com/stylelint/stylelint-config-standard/blob/6b317dd651422e9c487dfa6965ce21df086ccb9a/index.js#L4

https://github.com/stylelint/stylelint-config-recommended/blob/ca9a869788a3c23b602ce23404ce73a0489e0efd/index.js#L22

1 Like

I believe that rule is being triggered because you’re putting a less specific selector after a more specific one:

/** specificity of 0,0,5,1 */
.img-split .img-split-el .img-split-cont:hover .img-split-btn button { ... }

/** specificity of 0,0,4,1 */
.card.card-simple:hover .card-footer button { ... }

Both of these selectors target button, but the first one has higher specifity. Generally, you should structure your CSS so that it starts with lower specificity and moves to higher: Your arrangement inverts that, which is what Stylelint is complaining about.

Switching their order is the simplest fix, but if you replace the final item (currently button in both cases) with a selector that differs, that should also work. i.e.:

.img-split .img-split-el .img-split-cont:hover .img-split-btn .image-button { ... }
.card.card-simple:hover .card-footer .card-button { ... }
4 Likes

:sweat_smile: I most definitely misread the question. Nice explanation @alwaysblank!

2 Likes

Shouldn’t this apply only to same parent elements? Why should specifity matter if the parent is different? Otherwise, your solution works indeed yeah. But am still confused as to why this throws the error here since the parent selector is different and we’r targeting a totally different thing at the end.

From an HTML perspective, yes, you are targeting different elements, but from a CSS perspective you aren’t: both selectors target a button. The way CSS works and the way those selectors are written, both of them could target the same element–you just happen to know they don’t because you wrote the HTML. This is exactly the kind of ambiguity CSS specificity is intended to resolve. It’s also why I usually try to write CSS selectors that are as simplistic as possible: ideally only 1 to 2 elements per selector.

1 Like

Writing BEM helps eliminate seeing this. But I know BEM’s divisive.

2 Likes

Coming back to this as I’m still having issues with new projects, probably my sass is not perfect :slight_smile:

Basic example:

.navbar-dark {
  .navbar-toggler-icon {
    background-image: url('../images/x-white.svg');
  }

  &.collapsed {
    .navbar-toggler-icon {
      background-image: url('../images/burger-white.svg');
    }
  }
}

.navbar-light {
  .navbar-toggler-icon {
    background-image: url('../images/x.svg');
  }

  &.collapsed {
    .navbar-toggler-icon {
      background-image: url('../images/burger.svg');
    }
  }
}

Will throw

Expected selector ".navbar-light            no-descending-specificity
          .navbar-toggler-icon" to come before                                 
          selector ".navbar-dark.collapsed                                     
          .navbar-toggler-icon"

How can i solve this while keeping the eslint rule and without having to repeat my classes?

Think about your rule organization differently:

.navbar-toggler-icon {
    .navbar-dark & {
        background-image: url('../images/x-white.svg');
    }
    .navbar-light & {
        background-image: url('../images/x.svg');
    }
    &.collapsed {
        .navbar-dark & {
            background-image: url('../images/burger-white.svg');
        }
        .navbar-light & {
            background-image: url('../images/burger.svg');
        }
    }
}

In other words, think about your rules as describing the state of .navbar-toggler-icon instead of how .navbar-dark/light affects .navbar-toggler-icon. Without the dark.light options, your toggler icon has only two states: open, and collapsed. That would have rules like this:

.navbar-toggler-icon {
    background-image: url('../images/x.svg');
    &.collapsed {
       background-image: url('../images/burger.svg');
    }
}

Then all you’re doing is adding the light/dark modifiers to each state.

4 Likes

Hey, thanks for your answer: I was trying to make it work like that from start to be honest: but your example does not work because .collapsed is not on the .navbar-toggler-icon element.

The correct hierarchy would be something like:

.navbar-dark -> .navbar-toggler.collapsed -> .navbar-toggler-icon

:slight_smile:

In which package.json did you added it?
Maybe sage changed, but for me I needed to edit the node_modules/stylelint-config-recommended/index.js file to get rid of it.

Hey. Inside Sage theme root, package.json. It’s not there by default anymore it seems, so something like:

"stylelint": {
    "extends": "stylelint-config-standard",
    "rules": {
      "no-descending-specificity": null,
      "no-empty-source": null,
      "at-rule-no-unknown": [
        true,

I have to agree with the op here. I understand spcifity but sometimes this is broken.

Specifically I’m running into issues using a:hover. Which ever one I put first it still throws an error.
Expected selector “.nav li a” to come before selector “.banner a:hover” no-descending-specificity
Expected selector “.banner a” to come before selector “.nav li a” no-descending-specificity

.banner {
  background-color: $black;

  .navbar-brand {
    margin-right: 0;
  }

  a {
    color: $white;

    &:hover {
      color: $secondary;
    }
  }
}

.nav li {
  @extend .nav-item;

  a {
    @extend .nav-link;

    @include title;

    text-align: center;
  }
}

Ah yes, the good old descending specificity rule can cause depressions sometimes,
I disable it using stylelint comments:

/* stylelint-disable no-descending-specificity */
// your styles
/* stylelint-enable no-descending-specificity */

Sometimes it already helps to split up the styles, e.g. I use a style file for each Gutenberg block type and even for each block style of block type.

4 Likes

Solved my problem. Now I use it as first line of main.scss :blush:

1 Like