Sasgorilla
Sasgorilla

Reputation: 3130

How can I overrule an @extend directive in SCSS?

I have buttons inside a button group. Button groups come in different sizes, e.g. button-group-large. My goal is to avoid having to explicitly set the size on each button in a button group by letting it infer the size from the button group. That is, instead of

<div class="button-group-large">
  <button class="button-large">Foo</button>
  <button class="button-large">Bar</button>
</div>

I would rather write this:

<div class="button-group-large">
  <button>Foo</button>
  <button>Bar</button>
</div>

I can do it like this (in SASS):

.button-group-large {
  button { @extend .button-large; }
}

This works but creates the following problem. If one of my buttons has a special class, the .button-large class will take precedence. That is, if I do

<div class="button-group-large">
  <button class="special">Foo</button>
  <button>Bar</button>
</div>

Then any attribute that differs between .special and .button-large will get the .button-large value instead of the .special value. I want this to be the opposite; .special should win here. (As a real world example, .special might set the a "primary" button color).

Here's a jsfiddle illustrating the problem: https://jsfiddle.net/wwr63qmv/3/

As a reference point, Bootstrap button groups avoid this problem by requiring the classes to be set individually on each button, rather than on the button group. That may be the only answer. Is there some other way to get the .special class to win here?

Upvotes: 1

Views: 727

Answers (1)

Harry
Harry

Reputation: 89790

The problem that you are facing is not due to Sass or the @extend directive. It is how CSS works. If multiple selectors point to the same element then the most specific selector wins (or) if the specificity is same then the latest selector defined in the file wins.

The SCSS code in your fiddle will produce the below output when compiled:

.button, ul.button-group li button { /* check this selector */
  border: 1px solid black;
  color: black;
  border-radius: 2px;
}
.special-button { /* and this */
  border: 1px solid green;
  color: green;
}
ul.button-group {
  margin: 10px;
}
ul.button-group li {
  list-style-type: none;
  display: inline-block;
  padding: 5px;
}
p {
  font-family: monospace;
}

Both these selectors - ul.button-group li button and .special-button will target the button which has class as .special-button. The specificity of the first selector is 013 (because it has 1 class and 3 type selectors as part of the full complex selector) whereas that for the second selector is 010 (as it has only one class selector). So, by nature the first selector wins and only properties specified under it applies to the element.

In order for .special-button styles to apply, that selector's specificity should be increased. This can be done simply by changing the selector to ul.button-group li button.special-button but it means that the selector would now target only button with class='special-button' which are within a li whose ul parent has class='button-group'. That makes it very specific and means the properties won't apply if the button.special-button is outside ul.button-group li.

The other option would be to extend the .special-button also within the li like below:

ul.button-group {
  margin: 10px;
  li {
    list-style-type: none;
    display: inline-block;
    padding: 5px;
    button { 
      @extend .button;
      &.special-button { /* note this */
        @extend .special-button;
      }
    }
  }
}

Fiddle Demo

Upvotes: 1

Related Questions