ironfroggy
ironfroggy

Reputation: 8119

Can a Less mixin apply special rules to certain element types?

I have a mixin for button styling that I apply to both button-styled a tags and actual button tags, but I'm using flexbox vertical centering and this is broken in some versions of Webkit.

For example, here's how I might do it in Stylus, but i need to accomplish the same thing in Less:

.container {  
  btn(width) {
    display: flex;
    flex-direction: column;
    width: width;
    height: 50px;

    // How can I do this part, somehow, in Less?
    ../ button^[1..-1] {
      display: inline-block;
    }
  }

  .btn {
    btn(100px);
  }
}

The above is how I'm doing it in Stylus but I am hoping to find another way to get around the Less limitation. I tried appending to the selector, for example, :not(:not(button)) but this doesn't seem to be supported by browsers .

Upvotes: 2

Views: 437

Answers (1)

Harry
Harry

Reputation: 89760

Less (or any pre-processor) doesn't have any way to know what type of element is being represented by the selector and hence they cannot generate such rules without being specifically instructed by the developer. Also, Less doesn't support targeting a specific part of a parent selector and modifying it. So there is no direct conversion for the Stylus code given in question.


Having said that, there are a few alternate options that can be adopted to generate an output similar to that of the Stylus code.

Option 1: (Using CSS :not selector)

We can use the CSS :not (negation) selector to apply the flexbox styling only to .btn element that are not button elements.

The support for :not is available in latest versions of all browsers and IE supports it from 9+.

.container {  
  .btn-mixin(@width) {
    display: inline-block; /* default display is inline block */
    width: @width;
    height: 50px;
    &:not(button){ /* override for elements that are not buttons */
      display: flex;
      flex-direction: column;
    }
  }
  .btn {
    .btn-mixin(100px);
  }
}

This can't be written with the default display as flex because negation selector can be appended at the end whereas an element type selector cannot be. Below is a demo with the compiled CSS.

.container .btn {
  display: inline-block;
  width: 100px;
  height: 50px;
}
.container .btn:not(button) {
  display: flex;
  flex-direction: column;
}
<div class="container">
  <a href='#' class='btn'>Hello!</a>
  <a href='#' class='btn'>we</a>
  <a href='#' class='btn'>are</a>
  <a href='#' class='btn'>some</a>
  <a href='#' class='btn'>links</a>
</div>
<div class="container">
  <button class='btn'>Hello!</button>
  <button class='btn'>we</button>
  <button class='btn'>are</button>
  <button class='btn'>some</button>
  <button class='btn'>buttons</button>
</div>


Option 2: (Using Less parameterized mixins)

This is not an option that I recommend. Option 1 is in my opinion more straight-forward and simple. I am throwing this into the answer just to show conditional behavior can be achieved in Less. It uses the guards feature to check what the value of an argument (@elType) is and then apply styles.

.container {  
  .btn-mixin(@width; @elType: anchor) {
    & when not (@elType = anchor){
      display: inline-block;
    }
    & when (@elType = anchor){
      display: flex;
      flex-direction: column;
    }
    width: @width;
    height: 50px;
  }
  .btn {
    .btn-mixin(100px);
  }
  button.btn {
    .btn-mixin(200px, button);
  }
}

Why doesn't :not(:not(button)) work?

This selector wouldn't work (not yet atleast) because as of now, the negation :not selector accepts only simple selectors as arguments. :not(button) (which is the argument to the outer negation) is not a simple selector.

Upvotes: 1

Related Questions