Less and Bootstrap: How to Use a Span3 (Or Spanx [Any Number]) Class as a Mixin

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.

Media queries with less and Bootstrap

You are going to run into a few major problems in this approach.

First, there are issues in that Bootstrap is dynamically generating the .spanX series of classes, so they are not immediately accessible as mixins, as I note in this answer.

Second, some of Bootstrap's span code is not set by the .spanX series, but is done through an attribute selector of [class*="span"] (see, as of this writing, line 602 of the mixins.less file; they are also extensively used in the responsive files for the @media rules). This selector code would not be picked up by a .span12 inclusion of code, and if you are not putting .span12 in your code, then your code will not have those properties applied. So you need to work around that.

There may be other issues not immediately obvious, but the point is, if you want to use Bootstrap for grid purposes (especially responsive), it is probably best to use it as it is designed. If you do not, then it is best to go and copy the Bootstrap code and modify it as you desire to make your own reduced grid code (if your intent is to use less overall classes in your html).

How can I work around the need for Bootstrap 3's form-control class?

You realy should check a Django app that render all your Django forms as nice Boostrap forms, simply by using a tag in your template.

Its name is django-bootstrap3. Here's how you use it:

  1. Install django-bootstrap3
  2. Add bootstrap3 to your INSTALLED_APPS:

    INSTALLED_APPS = (
    ...
    'bootstrap3',
    ...
    )
  3. Update your template:

    1. load bootstrap3
    2. replace your {{form.as_p}} to {% bootstrap3_form form %}

before:

<form method="post" class="form-horizontal" action="" >
<div class="hidden-desktop">
<button type="submit" class="btn btn-primary"> Save</button>
</div>
{% csrf_token %}
{{ form.as_p }}
<div class="actions form-actions">
<button type="submit" class="btn btn-primary"> Save</button>
</div>
</form>

after:

{% extends "base.html" %} 
{% load bootstrap3 %}

<form method="post" class="form-horizontal" action="" >
<div class="hidden-desktop">
<button type="submit" class="btn btn-primary">{% bootstrap_icon "ok" %} Save</button>
</div>
{% csrf_token %}
{% bootstrap_form form %}
<div class="actions form-actions">
<button type="submit" class="btn btn-primary">{% bootstrap_icon "ok" %} Save</button>
</div>

Nothing else to do. Nothing to update in you form. No JavaScript hacks.

Twitter bootstrap 3 custom multiple theme's

Cause you mention @brand-primary i wrote an example for it.

First i look up all uses of @brand-primary

./type.less:  color: @brand-primary;
./type.less: color: darken(@brand-primary, 10%);
./variables.less:@brand-primary: #428bca;
./variables.less:@link-color: @brand-primary;
./variables.less:@component-active-bg: @brand-primary;
./variables.less:@btn-primary-bg: @brand-primary;
./variables.less:@pagination-active-bg: @brand-primary;
./variables.less:@label-primary-bg: @brand-primary;
./variables.less:@progress-bar-bg: @brand-primary;
./variables.less:@panel-primary-border: @brand-primary;
./variables.less:@panel-primary-heading-bg: @brand-primary;

@brand-primary will be only used in type.less once i copy this part to my new less file brand-primary.less:

.red-area
{
//type.less
.text-primary {
color: @brand-primary-red;
&:hover {
color: darken(@brand-primary-red, 10%);
}
}
}

Again i copy the rules with @link-color from different files to brand-primary.less and rename @link-color to @link-color-red. I copy the @link-hover-color cause this is defined by @link-hover-color: darken(@link-color, 15%); in variables.less.

And yes you're right this will effect many styles and will be complex. For example @component-active-bg will not used to set any style direct. @component-active-bg defines the value for @dropdown-link-active-bg, @nav-pills-active-link-hover-bg and @list-group-active-bg in variables.less

You will have to repeat this for every variable based on brand-primary

In some cases like the @link-color in thumbnails.less you will have to copy only a smaller part like:

.red-area
{
.thumbnail {
// Add a hover state for linked versions only
a&:hover,
a&:focus,
a&.active {
border-color: @link-color-red;
}
}
}

In other cases extending could help you to reduce the code to copy, see: https://stackoverflow.com/a/15573240/1596547

Doing this your final less file will look like:

//
// Load core variables and mixins
// --------------------------------------------------

@import "variables.less";
@import "mixins.less";

@brand-primary-red: #ff0000;
@link-color-red: @brand-primary-red;
@link-hover-color-red: darken(@link-color-red, 15%);
@component-active-bg-red: @brand-primary-red;
@dropdown-link-active-bg-red: @component-active-bg-red;
@nav-pills-active-link-hover-bg-red: @component-active-bg-red;
@list-group-active-bg-red: @component-active-bg-red;
@list-group-active-border-red: @list-group-active-bg-red;
@btn-primary-bg-red: @brand-primary-red;
@btn-primary-border-red: darken(@btn-primary-bg-red, 5%);
@pagination-active-bg-red: @brand-primary-red;
@label-primary-bg-red: @brand-primary-red;
@progress-bar-bg-red: @brand-primary-red;
@panel-primary-border-red: @brand-primary-red;
@panel-primary-heading-bg-red: @brand-primary-red;

.red-area
{
//type.less
.text-primary {
color: @brand-primary-red;
&:hover {
color: darken(@brand-primary-red, 10%);
}
}

//scaffolding.less
// Links

a {
color: @link-color-red;
text-decoration: none;

&:hover,
&:focus {
color: @link-hover-color-red;
text-decoration: underline;
}

&:focus {
.tab-focus();
}
}

//thumbnails.less

.thumbnail {
// Add a hover state for linked versions only
a&:hover,
a&:focus,
a&.active {
border-color: @link-color-red;
}
}

//buttons.less
// Make a button look and behave like a link
.btn-link {
color: @link-color-red;
font-weight: normal;
cursor: pointer;
border-radius: 0;

&,
&:active,
&[disabled],
fieldset[disabled] & {
background-color: transparent;
.box-shadow(none);
}
&,
&:hover,
&:focus,
&:active {
border-color: transparent;
}
&:hover,
&:focus {
color: @link-hover-color-red;
text-decoration: underline;
background-color: transparent;
}
&[disabled],
fieldset[disabled] & {
&:hover,
&:focus {
color: @btn-link-disabled-color;
text-decoration: none;
}
}
}

//nav.less
// Open dropdowns
.open > a {
&,
&:hover,
&:focus {
background-color: @nav-link-hover-bg;
border-color: @link-color-red;

.caret {
border-top-color: @link-hover-color-red;
border-bottom-color: @link-hover-color-red;
}
}
}

//dropdowns.less
// Active state
.dropdown-menu > .active > a {
&,
&:hover,
&:focus {
color: @dropdown-link-active-color;
text-decoration: none;
outline: 0;
background-color: @dropdown-link-active-bg-red;
}
}

//nav.less
.nav-pills {
> li {
float: left;

// Links rendered as pills
> a {
border-radius: @nav-pills-border-radius;
}
+ li {
margin-left: 2px;
}

// Active state
&.active > a {
&,
&:hover,
&:focus {
color: @nav-pills-active-link-hover-color;
background-color: @nav-pills-active-link-hover-bg-red;

.caret {
border-top-color: @nav-pills-active-link-hover-color;
border-bottom-color: @nav-pills-active-link-hover-color;
}
}
}
}
}

//list-group.less
// Linked list items
a.list-group-item {
color: @list-group-link-color;

.list-group-item-heading {
color: @list-group-link-heading-color;
}

// Hover state
&:hover,
&:focus {
text-decoration: none;
background-color: @list-group-hover-bg;
}

// Active class on item itself, not parent
&.active,
&.active:hover,
&.active:focus {
z-index: 2; // Place active items above their siblings for proper border styling
color: @list-group-active-color;
background-color: @list-group-active-bg-red;
border-color: @list-group-active-border-red;

// Force color to inherit for custom content
.list-group-item-heading {
color: inherit;
}
.list-group-item-text {
color: lighten(@list-group-active-bg, 40%);
}
}
}

//buttons.less
.btn-primary {
.button-variant(@btn-primary-color; @btn-primary-bg-red; @btn-primary-border-red);
}

//pagination.less
.pagination {
display: inline-block;
padding-left: 0;
margin: @line-height-computed 0;
border-radius: @border-radius-base;

> li {
display: inline; // Remove list-style and block-level defaults
> a,
> span {
position: relative;
float: left; // Collapse white-space
padding: @padding-base-vertical @padding-base-horizontal;
line-height: @line-height-base;
text-decoration: none;
background-color: @pagination-bg;
border: 1px solid @pagination-border;
margin-left: -1px;
}
&:first-child {
> a,
> span {
margin-left: 0;
.border-left-radius(@border-radius-base);
}
}
&:last-child {
> a,
> span {
.border-right-radius(@border-radius-base);
}
}
}

> li > a,
> li > span {
&:hover,
&:focus {
background-color: @pagination-hover-bg;
}
}

> .active > a,
> .active > span {
&,
&:hover,
&:focus {
z-index: 2;
color: @pagination-active-color;
background-color: @pagination-active-bg-red;
border-color: @pagination-active-bg-red;
cursor: default;
}
}

> .disabled {
> span,
> span:hover,
> span:focus,
> a,
> a:hover,
> a:focus {
color: @pagination-disabled-color;
background-color: @pagination-bg;
border-color: @pagination-border;
cursor: not-allowed;
}
}
}

//labels.less

.label-primary {
.label-variant(@label-primary-bg-red);
}

//progress-bars.less
// Bar of progress
.progress-bar {
background-color: @progress-bar-bg-red;
}

//panels.less

.panel-primary {
.panel-variant(@panel-primary-border-red; @panel-primary-text; @panel-primary-heading-bg-red; @panel-primary-border-red);
}

}

You could compile this file like theme.less or remove the imports and import it in boostrap.less

The final css will look like:

.red-area .text-primary {
color: #ff0000;
}
.red-area .text-primary:hover {
color: #cc0000;
}
.red-area a {
color: #ff0000;
text-decoration: none;
}
.red-area a:hover,
.red-area a:focus {
color: #b30000;
text-decoration: underline;
}
.red-area a:focus {
outline: thin dotted #333;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
a.red-area .thumbnail:hover,
a.red-area .thumbnail:focus,
a.red-area .thumbnail.active {
border-color: #ff0000;
}
.red-area .btn-link {
color: #ff0000;
font-weight: normal;
cursor: pointer;
border-radius: 0;
}
.red-area .btn-link,
.red-area .btn-link:active,
.red-area .btn-link[disabled],
fieldset[disabled] .red-area .btn-link {
background-color: transparent;
-webkit-box-shadow: none;
box-shadow: none;
}
.red-area .btn-link,
.red-area .btn-link:hover,
.red-area .btn-link:focus,
.red-area .btn-link:active {
border-color: transparent;
}
.red-area .btn-link:hover,
.red-area .btn-link:focus {
color: #b30000;
text-decoration: underline;
background-color: transparent;
}
.red-area .btn-link[disabled]:hover,
fieldset[disabled] .red-area .btn-link:hover,
.red-area .btn-link[disabled]:focus,
fieldset[disabled] .red-area .btn-link:focus {
color: #999999;
text-decoration: none;
}
.red-area .open > a,
.red-area .open > a:hover,
.red-area .open > a:focus {
background-color: #eeeeee;
border-color: #ff0000;
}
.red-area .open > a .caret,
.red-area .open > a:hover .caret,
.red-area .open > a:focus .caret {
border-top-color: #b30000;
border-bottom-color: #b30000;
}
.red-area .dropdown-menu > .active > a,
.red-area .dropdown-menu > .active > a:hover,
.red-area .dropdown-menu > .active > a:focus {
color: #ffffff;
text-decoration: none;
outline: 0;
background-color: #ff0000;
}
.red-area .nav-pills > li {
float: left;
}
.red-area .nav-pills > li > a {
border-radius: 4px;
}
.red-area .nav-pills > li + li {
margin-left: 2px;
}
.red-area .nav-pills > li.active > a,
.red-area .nav-pills > li.active > a:hover,
.red-area .nav-pills > li.active > a:focus {
color: #ffffff;
background-color: #ff0000;
}
.red-area .nav-pills > li.active > a .caret,
.red-area .nav-pills > li.active > a:hover .caret,
.red-area .nav-pills > li.active > a:focus .caret {
border-top-color: #ffffff;
border-bottom-color: #ffffff;
}
.red-area a.list-group-item {
color: #555555;
}
.red-area a.list-group-item .list-group-item-heading {
color: #333333;
}
.red-area a.list-group-item:hover,
.red-area a.list-group-item:focus {
text-decoration: none;
background-color: #f5f5f5;
}
.red-area a.list-group-item.active,
.red-area a.list-group-item.active:hover,
.red-area a.list-group-item.active:focus {
z-index: 2;
color: #ffffff;
background-color: #ff0000;
border-color: #ff0000;
}
.red-area a.list-group-item.active .list-group-item-heading,
.red-area a.list-group-item.active:hover .list-group-item-heading,
.red-area a.list-group-item.active:focus .list-group-item-heading {
color: inherit;
}
.red-area a.list-group-item.active .list-group-item-text,
.red-area a.list-group-item.active:hover .list-group-item-text,
.red-area a.list-group-item.active:focus .list-group-item-text {
color: #e1edf7;
}
.red-area .btn-primary {
color: #ffffff;
background-color: #ff0000;
border-color: #357ebd;
}
.red-area .btn-primary:hover,
.red-area .btn-primary:focus,
.red-area .btn-primary:active,
.red-area .btn-primary.active,
.open .dropdown-toggle.red-area .btn-primary {
color: #ffffff;
background-color: #d60000;
border-color: #285e8e;
}
.red-area .btn-primary:active,
.red-area .btn-primary.active,
.open .dropdown-toggle.red-area .btn-primary {
background-image: none;
}
.red-area .btn-primary.disabled,
.red-area .btn-primary[disabled],
fieldset[disabled] .red-area .btn-primary,
.red-area .btn-primary.disabled:hover,
.red-area .btn-primary[disabled]:hover,
fieldset[disabled] .red-area .btn-primary:hover,
.red-area .btn-primary.disabled:focus,
.red-area .btn-primary[disabled]:focus,
fieldset[disabled] .red-area .btn-primary:focus,
.red-area .btn-primary.disabled:active,
.red-area .btn-primary[disabled]:active,
fieldset[disabled] .red-area .btn-primary:active,
.red-area .btn-primary.disabled.active,
.red-area .btn-primary[disabled].active,
fieldset[disabled] .red-area .btn-primary.active {
background-color: #ff0000;
border-color: #357ebd;
}
.red-area .pagination {
display: inline-block;
padding-left: 0;
margin: 20px 0;
border-radius: 4px;
}
.red-area .pagination > li {
display: inline;
}
.red-area .pagination > li > a,
.red-area .pagination > li > span {
position: relative;
float: left;
padding: 6px 12px;
line-height: 1.428571429;
text-decoration: none;
background-color: #ffffff;
border: 1px solid #dddddd;
margin-left: -1px;
}
.red-area .pagination > li:first-child > a,
.red-area .pagination > li:first-child > span {
margin-left: 0;
border-bottom-left-radius: 4px;
border-top-left-radius: 4px;
}
.red-area .pagination > li:last-child > a,
.red-area .pagination > li:last-child > span {
border-bottom-right-radius: 4px;
border-top-right-radius: 4px;
}
.red-area .pagination > li > a:hover,
.red-area .pagination > li > span:hover,
.red-area .pagination > li > a:focus,
.red-area .pagination > li > span:focus {
background-color: #eeeeee;
}
.red-area .pagination > .active > a,
.red-area .pagination > .active > span,
.red-area .pagination > .active > a:hover,
.red-area .pagination > .active > span:hover,
.red-area .pagination > .active > a:focus,
.red-area .pagination > .active > span:focus {
z-index: 2;
color: #ffffff;
background-color: #ff0000;
border-color: #ff0000;
cursor: default;
}
.red-area .pagination > .disabled > span,
.red-area .pagination > .disabled > span:hover,
.red-area .pagination > .disabled > span:focus,
.red-area .pagination > .disabled > a,
.red-area .pagination > .disabled > a:hover,
.red-area .pagination > .disabled > a:focus {
color: #999999;
background-color: #ffffff;
border-color: #dddddd;
cursor: not-allowed;
}
.red-area .label-primary {
background-color: #ff0000;
}
.red-area .label-primary[href]:hover,
.red-area .label-primary[href]:focus {
background-color: #cc0000;
}
.red-area .progress-bar {
background-color: #ff0000;
}
.red-area .panel-primary {
border-color: #ff0000;
}
.red-area .panel-primary > .panel-heading {
color: #ffffff;
background-color: #ff0000;
border-color: #ff0000;
}
.red-area .panel-primary > .panel-heading + .panel-collapse .panel-body {
border-top-color: #ff0000;
}
.red-area .panel-primary > .panel-heading > .dropdown .caret {
border-color: #ffffff transparent;
}
.red-area .panel-primary > .panel-footer + .panel-collapse .panel-body {
border-bottom-color: #ff0000;
}

The css rules will be applied on everthing wrapped inside a .red-area class.
Notes:

  • the less code could be optimalized.
  • when copy all pieces by hand it won't be easy to handle updates of Bootstrap

Also read this answer to fit the best way of extending / copy classes: https://stackoverflow.com/a/19980145/1596547

CSS issue on Twitter Typeahead with Bootstrap 3

EDIT: Updated for Bootstrap 3.0
EDIT 2: Typeahead call was modified. See new jsfiddle

After playing around with the styling it looks like the form-control class doesn't quite line-up with the tt-hint. So I made sure the margins and borders line up. Taking Hieu Nguyen's answer and adding border-radius and support for input-small/input-large

CSS

.twitter-typeahead .tt-hint
{
display: block;
height: 34px;
padding: 6px 12px;
font-size: 14px;
line-height: 1.428571429;
border: 1px solid transparent;
border-radius:4px;
}

.twitter-typeahead .hint-small
{
height: 30px;
padding: 5px 10px;
font-size: 12px;
border-radius: 3px;
line-height: 1.5;
}

.twitter-typeahead .hint-large
{
height: 45px;
padding: 10px 16px;
font-size: 18px;
border-radius: 6px;
line-height: 1.33;
}

Script for input-small/input-large

$('.typeahead.input-sm').siblings('input.tt-hint').addClass('hint-small');
$('.typeahead.input-lg').siblings('input.tt-hint').addClass('hint-large');

Updated jsfiddle: http://jsfiddle.net/KrtB5/542/



Related Topics



Leave a reply



Submit