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
.
This is easy to accomplish with Less.js, but the real question is: "Should I mix structural grid classes with my non-structural classes?"
And the answer is no.
This is a bad idea, the advantage of a grid system is that it creates a separation of concerns between structural styling and other styling. I'm not saying "it should never, ever, ever be done". But in general, it shouldn't. I don't even like seeing this:
<div class="span3 sidebar">
<ul class="nav">
...
</ul>
</div>
Where the span3
is in the same div
as the .sidebar
class. The problem with this is that now your sidebar is not just "floating around" inside a column of the grid, it has become part of the grid - which (in general) makes it even more difficult to maintain your styles because of the workarounds you need to create to force this kind of styling to be responsive.