7

从 Bootstrap 源码学习 Less 编写方法

 3 years ago
source link: http://misaka.im/index.php/archives/4/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

中文站点与官方文档

官方给出的英文文档实在难啃,加上国内中文站点翻译又不给力,先放几个站点:

需要了解的一些事儿

前端世界里家喻户晓的Bootstrap.css 便是由Less源码编译得来的(当然这儿我并不讨论Sass项目)。从Less得到CSS,你可以使用构建工具或者带图形界面的编译器,如 GruntKoala,这都取决于你的工作环境。


首先来些开胃菜,Button 样式往往需要一些 背景、边框以及字体颜色,实际大小由 Button 内文字行高和数量决定。

参考文件
bootstrap-3.3.6/less/mixins/button.less

.button-variant 类在定义button的背景、边框以字体颜色的同时,也给不忘定义伪类和伪元素。来看看他们在Bootstrap 中用Less是怎么组合的。

// Button variants
//
// Easily pump out default styles,as well as:hover,:focus,:active,// and disabled options for all buttons
.button-variant(@color;
@background;
@border) {
    color: @color;
    background-color: @background;
    border-color: @border;
    &:hover, &:focus, &.focus, &:active, &.active, .open > .dropdown-toggle& {
        color: @color;
        background-color: darken(@background, 10%);
        border-color: darken(@border, 12%);
    }
    &:active, &.active, .open > .dropdown-toggle& {
        background-image: none;
    }
    &.disabled, &[disabled], fieldset[disabled] & {
        &, &:hover, &:focus, &.focus, &:active, &.active {
            background-color: @background;
            border-color: @border;
        }
    }
    .badge {
        color: @background;
        background-color: @color;
    }
} // Button sizes
.button-size(@padding-vertical;
@padding-horizontal;
@font-size;
@line-height;
@border-radius) {
    padding: @padding-vertical @padding-horizontal;
    font-size: @font-size;
    line-height: @line-height;
    border-radius: @border-radius;
}

这一段比较绕的,删减些重复的样式,不必要在意花括号内的具体样式。

.button-variant {
    :active, .active, .open > .dropdown-toggle & {
        background-image: none;
    }
    .disabled,
    [disabled],
    fieldset[disabled] & {
        :hover, :focus, .focus {
            background-color: #000;
        }
    }
}

手动编译之后得到

.button-variant:active,
.button-variant.active,
.open > .dropdown-toggle.button-variant {
    background-image: none;
}

.button-variant.disabled:hover,
.button-variant[disabled]:hover,
fieldset[disabled] .button-variant:hover,
.button-variant.disabled:focus,
.button-variant[disabled]:focus,
fieldset[disabled] .button-variant:focus,
.button-variant.disabled.focus,
.button-variant[disabled].focus,
fieldset[disabled] .button-variant.focus {
    background-color: #000;
}

哇塞好长一段,直接不看。

Nested Rules - 嵌套规则

先来看一个简单的例子:

.button-variant:active, .button-variant.active {
    background-image: none;
}

使用嵌套规则的同时,配合 & 使用可选择其父元素,

.button-variant:active,
.button-variant.active

& 符号使 button-variantactive 紧贴在一起,表达为
.button-variant:active(状态)(效果)的样式;
.button-varian和类.active同时被应用时元素的样式。
所有二层选择都会和一层选择紧贴在一起组成新的选择器,有多少个二层选择编译后就有多少种选择器情况。
举个例子

.A {
    &.B, &.C, &.D {
        color: #ABC;
    }
}

二层嵌套中,有三个类选择器 .B .C .D。这里的 & 符号紧跟着后面的选择器,编译后就像乘法因式分解一样。

.A.B, .A.C, .A.D {
    color: #ABC;
}
/*or */
.A.B {
    color: #ABC;
}
.A.C {
    color: #ABC;
}
.A.D {
    color: #ABC;
}

.A.B .A.C .A.D 两两必须成对存在此选择器才在HTML中生效。
HTML应该这么写,A B间存在空格。

<!--HTML-->
<p class="A B"></p>
<p class="A C"></p>
<p class="A D"></p>

Changing Selector Order - 更改选择器顺序

上面的两层嵌套很容易被看出,那么继续来看三成嵌套。我截取了上面栗子的一小段,先来解析一下这7行。

.button-variant {
    &:active, &.active, .open > .dropdown-toggle& {
        background-image: none;
    }
}

同样,用上述的方法可以简单地把2 3行编译出来。但是 第4行的选择器并没有在开头用到 & ,而在尾部。其中还用到了

> 子元素选择器

太复杂的话我选择把尾部的 & 拿掉之后再来理解,首部没有 & 那么两两不连贯,猜想应该是这样子的:

/*this is inaccuracy result */
.button-variant:active, .button-variant.active, .button-variant .open > .dropdown-toggle {
    background-image: none;
}

剩下尾部的 ** 我们不用胡乱猜,参见一下官方手册 Changing Selector Order - 更改选择器顺序
尾部的 & 可以让当前选择器直接成为新的父选择器,也就是成为当前父选择器的父选择器。
还是举个栗子:

.header {
    .menu {
        border-radius: 5px;
        //putting the & after current selector
        .no-borderradius & {
            background-image: url('images/button-background.png');
        }
    }
}

编译之后 本来是 子选择器的 .no-borderradius 摇身一变成了其父选择器的父选择器,放在了第一位。
要注意的是,新的后代选择顺序不是与原本的相反,而是当前选择器直接成为父选择器,就像移动到前面一样。

.header .menu {
    border-radius: 5px;
}

.no-borderradius .header .menu {
    /*now no-borderradius is a perent selector*/
    background-image: url('images/button-background.png');
}

这样,大概我可以猜到上一题的答案是:

/*this is inaccuracy result */
.button-variant:active,
.button-variant.active,
.open > .dropdown-toggle .button-variant {
    background-image: none;
}

等等!好像并不一样 .dropdown-toggle.button-variant 之间存在了一个空格,其选择效果却是有差别的。这肯定是&前后出现了空白写法才会到这这种问题。有没有发现:

.no-borderradius &

.dropdown-toggle&

选择器名称后面少一个空格。产生的结果是不同的:

/*has blank */
.open > .dropdown-toggle .button-variant {
    background-image: none;
}

/*none */
.open > .dropdown-toggle.button-variant {
    background-image: none;
}

最后我们再把文章开头剩下的Less解析完。

.button-variant {
    &.disabled, &[disabled], fieldset[disabled] & {
        &:hover, &:focus, &.focus {
            background-color: #000;
        }
    }
}

编译之后。每条规则先解析一层,从

.button-variant -> .disabled ->:hover

在选择器结尾处遇到 & 时就前置当前选择器

.button-variant -> fieldset[disabled] & ->:hover

前置 fieldset[disabled] , 注意伪类选择器应紧跟前面的内容。

fieldset[disabled] -> .button-variant ->:hover

这么走一轮,有9种不同的选择器方案,最终结果:

.button-variant.disabled:hover,
.button-variant[disabled]:hover,
fieldset[disabled] .button-variant:hover,
.button-variant.disabled:focus,
.button-variant[disabled]:focus,
fieldset[disabled] .button-variant:focus,
.button-variant.disabled.focus,
.button-variant[disabled].focus,
fieldset[disabled] .button-variant.focus {
    background-color: #000;
}



About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK