对Bootstrap 4 CSS组件架构和命名规则的学习
一、组件最高类名
1个单词的类名
BS4的组件,以组件名单词作为最高的类名,比如警告条,最高的类名是alert
,又比如徽章,最高类名就是badge
。
也就是说,组件与布局是解耦的,不管你把这个组件用在什么地方,这个组件的基本形态首先就是这个样子,之后你想为项目进行个性化定制,那么你可以给最高层追加定制类。
2个单词的类名
下面的btn-toolbar
就是两个单词组成最高组件类名,反而子元素的类名有1个单词的btn
,这其中的奥妙,就看你怎么看待你的组件了。如果你的组件里面的主要元素是子元素,整个组件就是这些子元素的集合,那么就像btn-toolbar
一样就好了,如果是警告条这种肯定是单个使用的组件,那么最高类名就用一个单词就OK。
<div class="btn-toolbar mb-3" role="toolbar" aria-label="Toolbar with button groups">
<div class="btn-group mr-2" role="group" aria-label="First group">
<button type="button" class="btn btn-secondary">1</button>
<button type="button" class="btn btn-secondary">2</button>
<button type="button" class="btn btn-secondary">3</button>
<button type="button" class="btn btn-secondary">4</button>
</div>
<div class="input-group">
<span class="input-group-addon" id="btnGroupAddon">@</span>
<input type="text" class="form-control" placeholder="Input group example" aria-label="Input group example" aria-describedby="btnGroupAddon">
</div>
</div>
二、对最高层的外观附加类、功能附加类、工具类
外观附加类
比如警告条,BS4的做法是,最高层除了alert
,还会紧跟着一个alert-primary
或者其他类,也就是对alert
进行外观定制化,一个基本的alert
可以拓展出无数的附加类。这种类的写法是,把主类放前面,附加词放后面,用短横线链接。
alert-primary
强调的是语境,分为了主要的、次要的、危险的、成功的等等这些词,如果还要给一个“可收起的”属性,怎么办?BS4给出的方案是再加一个alert-dismissible
,这就是功能附加类。
再比如,徽章可以这么写<span class="badge badge-pill badge-primary">Primary</span>
,有2个附加类,badge-pill表示像胶囊一样的形状,跟后面的语境外观不是一回事,所以要另外摘出来定义。
再说一个特殊的栗子<button type="button" class="btn btn-outline-primary">Primary</button>
,这是包边的按钮,有带色边线,里面是白色背景填充。为什么不写成<button type="button" class="btn btn-outline btn-primary">Primary</button>
呢?因为btn-primary
主要特征是填充带色的背景,跟包边是冲突的两套东西,所以没办法配合,只能是另外单独定义一套btn-outline-xxx
系列。
还有一个特殊的栗子是active
类,它并不是独立的工具类,而是外观附加类,因为它都是用于.btn.active {...}
这样的形式,所以没必要写成btn-active
了。同理的还有disabled
等。
功能附加类
在最高层div身上,为了实现不同的目的,可以施加多个类,比如alert-warning
负责外观表现,alert-dismissible
负责功能(可收起的),还有其他功能的话,可以继续加。
<div class="alert alert-warning alert-dismissible fade show" role="alert">
<strong>Holy guacamole!</strong> You should check in on some of those fields below.
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
工具类
最后我们还可以看到fade show
,这是2个工具类,可以对任何元素起作用,效果一致,fade
是渐变动效果,show
表示当前是显示的状态。如果你能做出跟具体组件无关的通用的工具类,也可以继续追加到后面。
又如mb-0
类,它表示margin-bottom:0;
,这个类的意义是什么呢?因为BS的原则是,上下两个模块拉开间距,一律采用margin-bottom
,不要写任何的margin-top
,原因是间距折叠效应,所以比如一系列<p>,都统一只设置margin-bottom
的值就可以了。于是有一个问题就是,最后一个<p>可能不需要margin-bottom
,怎么办?给最后的<p>加个mb-0
类就好了。
三、使用者自己对最高层的外观附加类、功能附加类、工具类
自定义外观附加类
使用者使用BS4的时候,可以给最高层再加一个外观附加类,比如alert-xxx
,这个类的作用应该是,在alert
和alert-primary
基础上,再做外观修改,比如改一改字体大小,改一改行高,等等。这个xxx
可以是更高级的概念,比如模块名、页面名,甚至网站名:
- 如果利用模块名,比如新闻列表,那可以叫
alert-newslist
,意思是新闻列表里的警告条,就应该是这样子的。 - 如果利用页面名,比如页面是投票页,那就可以是
alert-poll
,意思是,用在投票页的警告条就得是我写的这样的长相。 - 如果利用网站名,比如你这是新浪的页面,可以干脆叫做
alert-sina
,也就是说,我们新浪的警告条,就应该是这个标准。
自定义功能附加类
写到这里的时候还没想出好的例子,先不写了。
自定义工具类
也没想出好的栗子,先不写了。
最终,最高层的类的样子应当是:“主类 外观类 自定义外观类 功能类 自定义功能类 工具类 自定义工具类”这样的顺序,就对了。
四、上下级类怎么写
下级专门定义一个类的情况
从BS4来看,比如警告条,警告条里面有能点击的链接,这样形成了上下级类,BS4的方案是:
<div class="alert alert-primary" role="alert">
This is a primary alert with <a href="#" class="alert-link">an example link</a>. Give it a click if you like.
</div>
就是说警告条里的链接,它的类的写法就应该是alert-link
。前面还是主类,后面加功能名称。
BS的原则是:能不级联就不级联,尽量只给最顶级标签加各种类,这样方便管理,而且,这些类定义的样式尽量是能继承的样式。所以,BS直接给alert-link
设样式,不写成级联形式。这样,alert-link
继承了祖先元素的一部分样式,自己又定义了一部分样式,这差不多就够用了,没必要写成.alert .alert-link
这样的声明。
下级不专门定义类的情况
看一个栗子:
<div class="alert alert-success" role="alert">
<h4 class="alert-heading">Well done!</h4>
<p>Aww yeah, you successfully read this important alert message. This example text is going to run a bit longer so that you can see how spacing within an alert works with this kind of content.</p>
<hr>
<p class="mb-0">Whenever you need to, be sure to use margin utilities to keep things nice and tidy.</p>
</div>
<p>标签没有加任何样式,原因是BS4给全局<p>设置了统一的样式,从实践中,很可能你的组件需要跟全局不一样的行高,怎么办呢?应当定义一个类似于这样一个样式:
.alert-sina p {
line-height: 1.8;
}
这时候问题来了,为什么不给<p>加上alert-p
类?因为首先这个类好像BS官方类一样,容易混淆视听,其次,你这个组件的<p>的样式,虽然可能跟全局<p>的样式不一致,但总不至于同一个组件内的<p>与<p>互相样式都不一致吧?所以只要统一给组件内设置一个样式就够了。设置类的原则就是一定要尽量精简,也要尽量降低耦合。
接上面问题,为什么组件里面的<a>就得加上alert-link
呢?因为<p>的用途比<a>窄,<p>除了表示一段话,通常没别的作用,<a>就难讲了,有各种用途,所以<a>不可能统一样式,不可以写成.alert a
的样子。
三层及以上结构
Card确实是BS4里面比较复杂的一个组件了,我们看一下:
<div class="card" style="width: 20rem;">
<img class="card-img-top" src="..." alt="Card image cap">
<div class="card-body">
<h4 class="card-title">Card title</h4>
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
<a href="#" class="btn btn-primary">Go somewhere</a>
</div>
</div>
主类是card
没悬念。
顶部的图片是card-img-top
,为啥有个top
呢?因为还有一个类是card-img
。
card-body
可以看出,如果你的一个组件有上中下三部分,你就可以用-top、-body、-bottom,也可以用-header、-body、-footer。这个元素一般是做容器用,设置简单的样式即可,一般不作为级联样式表的父元素。
card-title
、card-text
单词都是表意的,很好理解不解释。除非为了避免歧义,否则card-title
不要写成card-body-title
,一共就2个单词连起来就完事。这就好比,你说“她的眼睛是蓝色的”,没有哪个神经病会说“她的脑袋上的眼睛是蓝色的”,因为眼睛只有一对,一定在她的脑袋上,所以不需要特意写出来。
类名用词的惯例:
-
一条一条的项目,通常就是
-item
了; -
上中下结构,上面提过,就是用
-top
、-body
、-bottom
,也可以用-header
、-body
、-footer
; -
若没有上中下结构,而是分为主体和主体旁边的一些零碎,那么主体可以用
-inner
,旁边的零碎可以另外设定具体名字。 -
如果语义是菜单,通常用
-menu
包裹一系列的-item
。
五、3个单词组成的类名
第3个单词作为修饰词的情况
看个栗子:
<div class="btn-group">
<button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
This dropdown's menu is right-aligned
</button>
<div class="dropdown-menu dropdown-menu-right">
<button class="dropdown-item" type="button">Action</button>
<button class="dropdown-item" type="button">Another action</button>
<button class="dropdown-item" type="button">Something else here</button>
</div>
</div>
dropdown-menu-right
是3个单词的类名,第三个单词right是修饰词。这跟BEM命名法是一致的。
3个单词连起来才描述清楚一个类的情况
看个栗子:
<form class="dropdown-menu p-4">
<div class="form-group">
<label for="exampleDropdownFormEmail2">Email address</label>
<input type="email" class="form-control" id="exampleDropdownFormEmail2" placeholder="email@example.com">
</div>
<div class="form-group">
<label for="exampleDropdownFormPassword2">Password</label>
<input type="password" class="form-control" id="exampleDropdownFormPassword2" placeholder="Password">
</div>
<div class="form-check">
<label class="form-check-label">
<input type="checkbox" class="form-check-input">
Remember me
</label>
</div>
<button type="submit" class="btn btn-primary">Sign in</button>
</form>
form-check-label
是3个单词组成的类,它的意思是form中的check容器中的label标签。通常有一个父元素叫form-check
。
form-check-input
也是3个单词组成的类,它的意思是form中的check容器中的input标签。通常有一个父元素叫form-check
。
总结
-
可以把组件当做一个人体来看。比如眼睛,它虽然属于头部,但你并不用强调眼睛是“头部的眼睛”。虽然不用强调,但是眼睛还是会继承头部的特性,比如头部的海拔高,所以你眼睛的海拔也高。这就算是继承。栗子可能不太恰当,理解意思即可。
-
如果用sass来写css,不要沉迷于sass的级联写法,因为组件化的特征就是尽量不级联。sass里的级联,最多是2级连一起就够了。所以,请努力让级联扁平起来。
-
想给一个组件加样式,应当尽量放在最顶级类里,然后多利用CSS的继承,能在最高级类里面定义的,一定要在最高级类里定义,遇到不能继承的,再在下级类里另写样式。
-
<p>这种标签,属于用的场合多,但意义简单,所以,要修改<p>的样式,不要给<p>身上放类,应该用
.xxx p {}
这种级联形式。反之,<a>就最好在它身上放类。 -
从dropdown的栗子可以看出,一个组件可以包含其他组件,这是允许的,其他组件的类名不要写成
dropdown-xxx-xxx
,这样等于你把好容易解耦的样式又耦合起来了,应当直接定义一个xxx-xxx
类,然后用.dropdown .xxx-xxx {}
来定义样式。 -
容器名字一般就是header/body/footer这种。
-
即使是很底层的标签,类名也不要繁复,除非有必要,否则应当尽量短。
form-check-input
之所以长,是因为它专门用于<input type="checkbox">,实在没办法简写成form-input
。 -
注意区分两个概念“模块”和“组件”。模块是什么?比如页面左侧导航,比如新闻列表,比如天气预报模块,等等。组件就好比螺丝,模块就好比各种型号的自行车,螺丝生产的时候有自己的工业标准,并不关心螺丝将来是用在哪个厂家的哪辆自行车上。所以,组件的标签上不要带模块名字,而是应该用级联写法,比如模块是评论框,那么模块最外层的类可以叫
comments
,它里面有几个组件,任何组件的类名都不要带comments字样。最后,用.comments .xxx {}
就搞定了样式。 -
有时候你会纠结模块的类名,比如评论区,到底类名应该是
comment-section
还是comment
。说白了,如果下级有类名叫comment
,那么顶级类名叫comment-section
是可以的,如果一个个的comment
形成了list的感觉,顶级类也可以叫comment-list
,如果下级并没有一个类名叫comment
,那么顶级类名就可以直接叫comment
,应该是没问题的。所以,一切看情况而定,类名尽量简单。
附录一:id什么时候用?怎么写?
锚链接定位
<a href="#header">跳转到header</a>
配合上<a id="header"></a>
实现锚链接。
<label for="...">
<label for="a"><input id="a" type="checkbox"></label>
用于让label操作控件。
当做JS的操作绑定元素
比如BS里面有id="btnGroupDrop1"
和aria-labelledby="btnGroupDrop1"
:
<button id="btnGroupDrop1" type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Dropdown
</button>
<div class="dropdown-menu" aria-labelledby="btnGroupDrop1">
<a class="dropdown-item" href="#">Dropdown link</a>
<a class="dropdown-item" href="#">Dropdown link</a>
</div>
由于JS的元素选择器查找id比查找其他的形式要快,所以给被选择的元素加上id是个好习惯。
另外,最好给id名的前面加上js-
,这表示这个元素是一个js钩子。
当做最大的模块名
比如你的页面有5个大模块,比如header、footer、main等等的,你除了可以用<header>标签外,也可以用<div id="header">,当然了,这种情况下能不用id就别用,因为id主要不是干这个用的。
id的写法
可以有2种,一种是驼峰写法btnGroupAddon2
,一种是下划线写法btn_group_addon_2
,具体用哪种都可以,真心没有任何区别,所以项目内部统一一种即可。我个人偏向于下划线写法,因为我喜欢给变量名使用驼峰写法,这样的话两种写法在js中就可以一眼区分。
一般不要用连接线写法,因为容易跟class混淆,而且id名可以直接拿来当js的window对象的属性,也就是window.btn_group_addon_2
就指向该元素,但是window.btn-group-addon-2
是非法的写法。
附录二:注释什么时候写?
业界目前认为最正确的做法,是只给hack做注释,因为你不注释,我真的不好理解你这个hack是干嘛的。其他情况下,应当通过类名就可以让人明白这个类是干什么的。