3

I have a button: <button class="outline">Outline</button> which I want to set the outline on, but all of the other buttons should not have outline.

If give it a class b1 and do:

[dir="ltr"] .b1 {
    border: 0;
}
[dir="ltr"] .b1.outline {
    border-width: 1px;
}

It does not have an outline.

However, if give it a class b2 and do:

.b2 {
  border: 0;
}
.b2.outline {
  border-width: 1px;
}

It does have an outline!

Codepen to see for yourselves: https://codepen.io/anon/pen/gRMBdZ

Why does that happen?

Amit
  • 5,924
  • 7
  • 46
  • 94
  • Specificity:: https://stackoverflow.com/questions/4072365/css-understanding-the-selectors-priority-specificity – Turnip Jun 13 '17 at 14:30
  • Remove the space before `.b1` and the behavior is the same. – Anuga Jun 13 '17 at 14:32
  • This happens because of your selector. With `[dir="ltr"]` just before `.b1` it has a higher priority than `.b2`. So the style in `button.outline` has also higher priority than `.b2` and it overrides `border: 0;`. – Huelfe Jun 13 '17 at 14:33
  • @Turnip I understand specificity. It however does not seem to be the problem as "[dir=ltr] .b1.outline" > "[dir=ltr] .b1" > – Amit Jun 13 '17 at 14:33
  • 1
    It is absolutely the problem. `button.outline` is more specific than `.b2` so you never remove the border colour. – Turnip Jun 13 '17 at 14:34
  • 1
    `border-width: 1px;` alone will not set a border. You are removing the colour and border style in `[dir="ltr"] .b1` – Turnip Jun 13 '17 at 14:36
  • 1
    @Amit, there's really nothing wrong with your CSS specificty. `[dir="ltr"] .b1.outline {}` will always be stronger than `[dir='ltr'] .b1{}`. You just need to make sure all required properties of border are set for it to display. Cool question and interesting edge case, btw. Cheers! – tao Jun 13 '17 at 14:55
  • "Codepen to see for yourselves" — Provide a [mcve] **in the question itself** – Quentin Jun 13 '17 at 16:01

5 Answers5

4

This has to do with specifity and the use of border: 0;. The specificity of the two sets of selectors applies border: 0; in a different order than one another. border is a shorthand property used to apply border-width, border-style and border-color. Applying 0 or none will remove the styles for all of those properties.

In your CodePen you have button.outline which is playing a role in the visibility of the button's border.

Your first set is applied in this order:

  1. button.outline - border-style and border-color properties applied
  2. [dir="ltr"] .b1 - border properties removed with border: 0;
  3. [dir="ltr"] .b1.ouline - border-width applied, no color or style

Border is not visible even though there is a width because the rest of the border properties do not have values that would make it visible, like color and style.


Your second set is applied in this order:

  1. .b2 - border properties removed with border: 0;
  2. button.outline - border-style and border-color properties applied
  3. .b2.ouline - border-width applied

Border is visible because we have border properties that make it visible, i.e. width, color, style.

hungerstar
  • 21,206
  • 6
  • 50
  • 59
  • 1
    You really did not provide a proper answer to the question. The question was: why isn't [`dir="ltr"] .b1.outline {}` applying over `[dir="ltr"] .b1 {}`. And the answer is: it is applying for `border-width` correctly. However, the `border-style` remains set to `initial` by `border:0;` property. Your answer doesn't even touch the core of the problem. – tao Jun 13 '17 at 14:48
  • It's still a specificity issue when it comes to the specificity of `border: 0;` between the `.b1` and .`b2` selectors. Though I agree the answer should go into more detail in respect to the `border` property. I'll refine it. – hungerstar Jun 13 '17 at 14:55
  • I didn't quite welcome the *"it's still a specificity issue"* reply from you, as well as your initial *"You should brush up on CSS specificity"* opener, when that was not really the issue. I usually appreciate your answers/presence on [so]. I apologize for being aggressive. – tao Jun 13 '17 at 17:34
3

It's because

border:0;

... translates to:

border-top-color: initial;
border-top-style: initial;
border-top-width: 0px;
border-right-color: initial;
border-right-style: initial;
border-right-width: 0px;
border-bottom-color: initial;
border-bottom-style: initial;
border-bottom-width: 0px;
border-left-color: initial;
border-left-style: initial;
border-left-width: 0px;
border-image-source: initial;
border-image-slice: initial;
border-image-width: initial;
border-image-outset: initial;
border-image-repeat: initial;

This means the border-style property is set to initial, which defaults it to none. The width applies, but the style is initial = none. You need to set it any render-able value so it applies a visible border:

[dir="ltr"] .b1 {
  border: 0;
}

[dir="ltr"] .b1.outline {
  border-width: 1px;
  border-style: solid;
}
tao
  • 82,996
  • 16
  • 114
  • 150
  • 1
    (Not OP btw.) Yep. `[dir="ltr"] .b1.outline` *is* higher cascade value than `[dir="ltr"] .b1`, but it doesn't matter because `border-style` has been overridden by the `border` shorthand. – Joseph Marikle Jun 13 '17 at 14:39
  • @JosephMarikle That's exactly what I said. `border-style` is set to `initial`, which is `none`, by `border: 0;`. What exactly is not clear? – tao Jun 13 '17 at 14:40
  • Not sure, man. I think this is the right answer. Not sure why it got a downvote. – Joseph Marikle Jun 13 '17 at 14:42
  • I re-worded it so it's clearer. But it was correct in the first explanation. Just not so clear and bold. :) – tao Jun 13 '17 at 14:44
2

When you add the [dir="ltr"], it changes the order in which rules are processed.

For Button 1 (no outline):

[dir="ltr"] .b1.outline {
    border-width: 1px;
}

[dir="ltr"] .b1 {
    border: 0;
}

button.outline {
    border-style: solid;
    border-color: #387ef5;
    color: #387ef5;
    background-color: transparent;
}

button {
    font-size: 1.7rem;
}

And for Button 2 (outline):

.b2.outline {
    border-width: 1px;
} 

button.outline {
    border-style: solid;
    border-color: #387ef5;
    color: #387ef5;
    background-color: transparent;
}

.b2 {
    border: 0;
}

button {
    font-size: 1.7rem;
}

Notice how button.outline is the 3rd rule on Button 1 and the 2nd rule on Button 2 since the specificity changed.

Jennifer Goncalves
  • 1,442
  • 1
  • 12
  • 29
0

Take in count the rules of CSS. If you have more levels of nesting, this will be the priority. In your case, .b2 is the basic level, so when you set button.outline this will be more important than .b2 rule, for that reason all .b2 elements will have the outline. If you want to rewritte the rules, you have to call button.b2.outline instead only .b2

    /* BASE CLASSES */
    button {
        font-size: 1.7rem;
    }
    .outline { /* Only use one level */
        border-style: solid;
        border-color: #387ef5;
        color: #387ef5;
        background-color: transparent;
    }

    /* B1 + DIRECTION */
    [dir="ltr"] .b1 {
        border: 0;
    }
    [dir="ltr"] .b1.outline {
        border-width: 1px;
    }

    /* B2 NO DIRECTION */
    button.b2 { /* Be more specific to replace previous rule */
      border: 0;
    }
    button.b2.outline {
      border-width: 1px;
    }
p1errot
  • 41
  • 1
  • 4
0

Because the styles with the [dir="ltr"] selector have higher precedence.

When you set:

[dir="ltr"] .b1 {border: 0}

You make all border-specific declarations in button.outline {...} obsolete. Therefore you have a border-width, but neither border-style nor border-color which are just as well necessary.

wortwart
  • 3,139
  • 27
  • 31