Less and Bootstrap: how to use a span3 (or spanX [any number]) class as a mixin?

New Answer (requires LESS 1.4.0)

What you actually desire is something known as extending in LESS and SASS terminology. For example, you want an HTML element (just an example)…

<div class="myclass"></div>

…to fully behave as if it had a span3 class from bootstrap added to it, but without actually adding that class in the HTML. This can be done in LESS 1.4.0 using :extend(), but still not easily, mainly because of the dynamic class generation of bootstrap will not be picked up by :extend().

Here is an example. Assume this initial LESS code (not dynamically generated .span3 classes as bootstrap does):

.span3 {
  width: 150px;
}

.someClass .span3 {
  font-size: 12px;
}

.someOtherClass.span3 {
  background: blue;
}

You add this LESS code in 1.4.0:

.myclass {
  &:extend(.span3);
}

Which produces this CSS:

.span3,
.myclass {
  width: 150px;
}
.someClass .span3 {
  font-size: 12px;
}
.someOtherClass.span3 {
  background: blue;
}

NOTE how it did not automatically extend the other instances of .span3. This is different than SASS, but it only means you need to be a bit more explicit in extending. This has the advantage of avoiding excessive CSS code bloat.

To fully extend, simply add the all keyword in the extend() (this is updated from my original code, as I was unaware of the all option):

.myclass {
  &:extend(.span3 all);
}

Which produces this:

.span3,
.myclass {
  width: 150px;
}
.someClass .span3,
.someClass .myclass {
  font-size: 12px;
}
.someOtherClass.span3,
.someOtherClass.myclass {
  background: blue;
}

That makes your .myclass fully equivalent (in my example) to the .span3 class. What this means in your case, however, is that you need to redefine any dynamic class generations of bootstrap to be non-dynamic. Something like this:

.span3 {
  .span(3);
}

This is so the :extend(.span3) will find a hard coded class to extend to. This would need to be done for any selector string that dynamically uses .span@{index} to add the .span3.

Original Answer

This answer assumed you desired to mixin properties from a dynamically generated class (that is what I thought your issue was).

Okay, I believe I discovered your issue. First of all, bootstrap defines the .spanX series of classes in the mixins.less file, so you obviously need to be sure you are including that in your bootstrap load. However, I assume it is a given that you have those included already.

Main Problem

The main issue is how bootstrap is generating those now, through a dynamic class name in a loop. This is the loop that defines the .spanX series:

.spanX (@index) when (@index > 0) {
      .span@{index} { .span(@index); }
      .spanX(@index - 1);
 }
.spanX (0) {}

Currently, because the class name itself is being dynamically generated, it cannot be used as a mixin name. I don’t know if this is a bug or merely a limitation of LESS, but I do know that at present time of writing, any dynamically generated class name does not function as a mixin name. Therefore, .span3 may be in the CSS code to put as a class in your HTML, but it is not directly available to access for mixin purposes.

The Fix

However, because of how they have structured the code, you can still get what you need, because as you can see above in the loop code, they use a true mixin itself to define the code for the .spanX classes. Therefore, you should be able to do this:

.myclass {
    .span(3);
    // other rules...
}

The .span(3) code is what the loop is using to populate the .span3 class, so calling it for your classes will give the same code that .span3 has. Specifically bootstrap has this defined in mixins.less for that mixin:

.span (@columns) {
  width: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1));
  *width: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%);
}

So you will get the width properties for the .span3 in your .myclass.

Leave a Comment