自从开始做前端开发以来,我发现在开发页面的时候,总是有一个问题十分影响自己的开发效率,这个问题就是css的命名,主要是指css类选择器的命名。这个问题主要体现在:第一,有的内容你压根想不出用什么名字来给它命名,因为一般命名总是考虑语义化,好让其他人看到这个css类的名字就知道它是作用于哪一个内容的,但是由于网页内容的复杂性和多样性,你很难保证每个部分都能起一个合适的名字,即使你最终迫不得已想出了一个名字,也会有这个名字是否是最合适的这种纠结存在,而且最要命的是,这个命名的过程是一项非常辛苦的脑力活动,会耗费掉很多脑细胞,这一件很不值得的事情;第二,由于命名的时候是语义化的命名,这一点可能会阻碍css代码的重用,比如说某一个网页的内容用.title来描述它的样式,这个title包含了2条规则,{font-size: 14px; line-height: 20px},此时网页的另一个内容可能需要跟这个title具有一模一样的样式,但是从另一个内容所处的网页位置来说,可能用.desc来命名才是更合适的选择,这个时候,我相信喜欢语义化命名的人肯定会把那个内容再单独起一个css类desc,然后把title的样式复制过来,结果就导致css文件中会存在两份相同的样式规则,只是命名不同而已,这样代码就重复了。
解决这个问题的方法就是采用面向属性的css命名,把那些我们实在想不出名字的,而且没有必要起名字的,并且可以只用单一的css属性或者组合的单一css属性来描述的部分,通通都用面向属性的css类来控制样式,让你从烦乱的css命名的漩涡中彻底解放出来,除了提高你的工作效率,最重要的是减少你脑细胞的损耗,让你不会那么辛苦。
首先要声明,面向属性的css命名这个思想不是我的原创,它来自于张鑫旭的博客。我是当时比较纠结于css的命名问题,然后找到了他的两篇文章,对我重新认识css的命名以及如何组织全站的css有很多的帮助,这两篇文章分别是:
精简高效的CSS命名准则/方法
我是如何对网站CSS进行架构的
你可以先去通过他的文章了解这个命名思想的起源,再回来看我的一些总结跟应用。
这个方法,简单来说,就是直接以css属性简写作为css的类名,在使用的时候,通过使用一个或组合多个这样的简写形式的css类来达到控制样式的目的。比如说网页中有一个网页内容,是一段居中的文本,大小为12px,行高为20px,段前后间距分别为10px和15px,那么就可以定义一下这些简单的属性类:
.tc {text-align: center;}.f12 {font-size: 12px;}.lh20 {line-height: 20px;}.mt10 {margin-top: 10px;}.mtb15 {margin-bottom: 15px;}
在页面中使用的时候,直接组合以上这些css属性类即可:
<p class="tc f12 lh20 mt10 mb15">…</p>
这就是这种属性命名方法的具体使用方式。
css中有很多的属性都可以采用这种方法来命名。在张鑫旭的博客中,他把自己的这套方法总结成了一个开源的css库:https://github.com/zhangxinxu/zxx.lib.css/blob/master/zxx.lib.css。我们可以先从他这个库来了解他本人是如何组织这些不同的css属性类的,然后再来讨论这其中的一些问题。
这是他的开源库中,采用面向属性命名的全部css类(以下代码仅是为了阅读本文方便才引用,如果是想在实际工作中使用,最好是关注张鑫旭本人的github或者博客,因为他会不断地优化自己的东西):
/* display */.dn{display:none;}.di{display:inline;}.db{display:block;}.dib{display:inline-block;}div.dib{*display:inline; *zoom:1;}/* other block level tag(eg. p, li, h1~h6), using 'inline_any' instead *//* height */.h0{height:0;}.h16{height:14px;}.h16{height:16px;}.h18{height:18px;}.h20{height:20px;}.h22{height:22px;}.h24{height:24px;}.h30{height:30px;}/* width *//* fixed width value */.w20{width:20px;}.w50{width:50px;}.w70{width:70px;}.w100{width:100px;}.w120{width:120px;}.w140{width:140px;}.w160{width:160px;}.w180{width:180px;}.w200{width:200px;}.w220{width:220px;}.w250{width:250px;}.w280{width:280px;}.w300{width:300px;}.w320{width:320px;}.w360{width:360px;}.w400{width:400px;}.w460{width:460px;}.w500{width:500px;}.w600{width:600px;}.w640{width:640px;}.w700{width:700px;}/* percent width value */.pct10{width:10%;}.pct15{width:15%;}.pct20{width:20%;}.pct25{width:25%;}.pct30{width:30%;}.pct33{width:33.3%;}.pct40{width:40%;}.pct50{width:50%;}.pct60{width:60%;}.pct66{width:66.6%;}.pct70{width:70%;}.pct75{width:75%;}.pct80{width:80%;}.pct90{width:90%;}.pct100{width:100%;}/* line-height */.lh0{line-height:0;}.lh16{line-height:14px;}.lh16{line-height:16px;}.lh18{line-height:18px;}.lh20{line-height:20px;}.lh22{line-height:22px;}.lh24{line-height:24px;}.lh30{line-height:30px;}/* margin */.m0{margin:0;}.ml1{margin-left:1px;}.ml2{margin-left:2px;}.ml5{margin-left:5px;}.ml10{margin-left:10px;}.ml15{margin-left:15px;}.ml20{margin-left:20px;}.ml30{margin-left:30px;}.mr1{margin-right:1px;}.mr2{margin-right:2px;}.mr5{margin-right:5px;}.mr10{margin-right:10px;}.mr15{margin-right:15px;}.mr20{margin-right:20px;}.mr30{margin-right:30px;}.mt1{margin-top:1px;}.mt2{margin-top:2px;}.mt5{margin-top:5px;}.mt10{margin-top:10px;}.mt15{margin-top:15px;}.mt20{margin-top:20px;}.mt30{margin-top:30px;}.mb1{margin-bottom:1px;}.mb2{margin-bottom:2px;}.mb5{margin-bottom:5px;}.mb10{margin-bottom:10px;}.mb15{margin-bottom:15px;}.mb20{margin-bottom:20px;}.mb30{margin-bottom:30px;}/* margin negative */.ml-1{margin-left:-1px;}.mr-1{margin-right:-1px;}.mt-1{margin-top:-1px;}.mb-1{margin-bottom:-1px;}.ml-3{margin-left:-3px;}.mr-3{margin-right:-3px;}.mt-3{margin-top:-3px;}.mb-3{margin-bottom:-3px;}.ml-20{margin-left:-20px;}.mr-20{margin-right:-20px;}.mt-20{margin-top:-20px;}.mb-20{margin-bottom:-20px;}/* padding */.p0{padding:0;}.p1{padding:1px;}.pl1{padding-left:1px;}.pt1{padding-top:1px;}.pr1{padding-right:1px;}.pb1{padding-bottom:1px;}.p2{padding:2px;}.pl2{padding-left:2px;}.pt2{padding-top:2px;}.pr2{padding-right:2px;}.pb2{padding-bottom:2px;}.pl5{padding-left:5px;}.p5{padding:5px;}.pt5{padding-top:5px;}.pr5{padding-right:5px;}.pb5{padding-bottom:5px;}.p10{padding:10px;}.pl10{padding-left:10px;}.pt10{padding-top:10px;}.pr10{padding-right:10px;}.pb10{padding-bottom:10px;}.p15{padding:15px;}.pl15{padding-left:15px;}.pt15{padding-top:15px;}.pr15{padding-right:15px;}.pb15{padding-bottom:15px;}.p20{padding:20px;}.pl20{padding-left:20px;}.pt20{padding-top:20px;}.pr20{padding-right:20px;}.pb20{padding-bottom:20px;}.p30{padding:30px;}.pl30{padding-left:30px;}.pt30{padding-top:30px;}.pr30{padding-right:30px;}.pb30{padding-bottom:30px;}/* border-color name rule: border(b)-position(l/r/t/b/d)-width(null/2)-style(null/sh)-color(first one letter/first two letter) |-> All colors are safe color*/.bdc{border:1px solid #ccc;}.blc{border-left:1px solid #ccc;}.brc{border-right:1px solid #ccc;}.btc{border-top:1px solid #ccc;}.bbc{border-bottom:1px solid #ccc;}.bdd{border:1px solid #ddd;}.bld{border-left:1px solid #ddd;}.brd{border-right:1px solid #ddd;}.btd{border-top:1px solid #ddd;}.bbd{border-bottom:1px solid #ddd;}.bde{border:1px solid #eee;}.ble{border-left:1px solid #eee;}.bre{border-right:1px solid #eee;}.bte{border-top:1px solid #eee;}.bbe{border-bottom:1px solid #eee;}/* background-color name rule: bg - (key word/Hex color) |-> All colors are safe color */.bgwh{background-color:#fff;}.bgfb{background-color:#fbfbfb;}.bgf5{background-color:#f5f5f5;}.bgf0{background-color:#f0f0f0;}.bgeb{background-color:#ebebeb;}.bge0{background-color:#e0e0e0;}/* safe color */.g0{color:#000;}.g3{color:#333;}.g6{color:#666;}.g9{color:#999;}.gc{color:#ccc;}.wh{color:white;}/* font-size */.f0{font-size:0;}.f12{font-size:12px;}.f13{font-size:13px;}.f14{font-size:14px;}.f16{font-size:16px;}.f18{font-size:18px;}.f20{font-size:20px;}.f24{font-size:24px;}.f28{font-size:28px;}/* font-family */.fa{font-family:Arial;}.ft{font-family:Tahoma;}.fv{font-family:Verdana;}.fs{font-family:Simsun;}.fl{font-family:'Lucida Console';}.fw{font-family:'Microsoft Yahei';}/* font-style */.n{font-weight:normal; font-style:normal; white-space: normal;}.b{font-weight:bold;}.i{font-style:italic;}/* text-align */.tc{text-align:center;}.tr{text-align:right;}.tl{text-align:left;}.tj{text-align:justify;}/* text-decoration */.tdl{text-decoration:underline;}.tdn,.tdn:hover,.tdn a:hover,a.tdl:hover{text-decoration:none;}/* letter-spacing */.lt-1{letter-spacing:-1px;}.lt0{letter-spacing:0;}.lt1{letter-spacing:1px;}/* white-space */.nowrap{white-space:nowrap;}/* word-wrap */.bk{word-wrap:break-word;}/* vertical-align */.vm{vertical-align:middle;}.vtb{vertical-align:text-bottom;}.vb{vertical-align:bottom;}.vt{vertical-align:top;}.v-1{vertical-align:-1px;}.v-2{vertical-align:-2px;}.v-3{vertical-align:-3px;}.v-4{vertical-align:-4px;}.v-5{vertical-align:-5px;}/* float */.l{float:left;}.r{float:right;}/* clear */.cl{clear:both;}/* position */.rel{position:relative;}.abs{position:absolute;}/*z-index*/.zx1{z-index:1;}.zx2{z-index:2;}/* cursor */.poi{cursor:pointer;}.def{cursor:default;}/* overflow */.ovh{overflow:hidden;}.ova{overflow:auto;}/* visibility */.vh{visibility:hidden;}.vv{visibility:visible;}/* opacity */.opa0{opacity:0; filer:alpha(opacity=0);}/* zoom */.z{*zoom:1;}
首先他这部分代码里面,包含了我们在网页开发中大部分常用的css属性,如display,height,margin,padding,border,color,font-size等。在属性类的命名上,基本上都是采取属性跟属性值的缩写。其他可说明的是:
1)width除了有固定值的属性类定义外,还包含了百分比的属性类定义,毕竟这个在实际工作中也时有用到;
2)margin,border,padding由于包含上下左右相关的属性,所以他还提供了针对上下左右单边的属性类,方便单独使用。
另外他的代码都有浏览器兼容性方面的考虑,所以要是在自己的工作中用的话,最好是参考着他的来。
这种方法在我使用之前,我比较顾虑的是它的可维护性。因为这些属性类很多都包含属性值,比如.f12{font-size: 12px},所以在html元素的class属性值就肯定会包含f12这样的css属性类名,假如要修改的对应的属性值该怎么办呢?那么就需要修改三个地方才行:首先是属性值定义的地方,第二是属性类名定义的地方,最后就是在html中使用的地方。当时想到这个问题的时候,我觉得这种方法不可行,因为在实际工作中,尼玛UI岗位的同事改设计的情况太多了,那样的话,页面上用到这种属性命名类的地方都要经常改来改去。
但是后来我发现,即使不用这种命名方法,我还是改变不了UI调整设计图的情况,而且只要设计图一改,甚至我的html结果以及我那些采用语义化命名的css类都要改,那个麻烦程度其实比用属性命名类的方法更厉害,所以我最后就开始在项目中尝试这个方法。结果发现,其实特别好用,尤其是做些文本类的排版,垂直布局,分栏布局,以及百分比布局等特别简单高效,前面提到的那个维护的问题也很小。我有两个方法来解决来那个问题:
1)假如原先用f12,后来设计把font-size改成14px,那么我只用再增加一个f14即可,再把原先html中需要把f12替换成f14的地方,换成f14即可。如果f12没有别的地方用到了,我会考虑把f12再删掉。
2)假如原先用f54,由于这种属性类并不具备通用性,所以我可能考虑直接把f54替换成我需要的属性类,比如f52。
在本文本部分的最后,我还会给出自己关于这种使用方法的建议以及对维护问题的处理补充。现在先回来再看看张鑫旭的这些代码,我从个人的角度,结合自己在项目中的实际情况,来说下需要我们改善下的地方。
1)有些采用固定值的属性类有多余的,也有缺少的;比如没有.h28表示height: 28px的,.w100到.w700可能都是用不到的
2)跟颜色相关的可能大部分都不一定符合项目需求,尤其是那种做出来完全没有任何规范的设计图,肉眼看上去相同的颜色,实际上却是不一样的颜色;相同的版本,在不同的页面,有可能也使用了不同的颜色;甚至是那种色系比较丰富的设计。这些属性类包括border,background,color。当然纯黑色和纯白色还是可以统一起来的,毕竟这两个颜色基本上各个网站都会用到。
3)还有些属性类也是多余的,比如font-family,因为我在项目中有用less,font-family是在别的地方定义的,所以这里就不需要了。当然你要是觉得这个还是有用得着的话,可以考虑留下。
4)还可以考虑增加些其它的css属性类,比如border-radius以及flex等。border-radius现在用的已经很普遍了,flex在移动端也有可以用的到的地方。这个就得根据自己的项目实际情况,慢慢增加了。
综合以上这些内容,我对于这种面向属性命名的方法建议如下:
1)首先肯定是得以张鑫旭的这套代码为基础;
2)对于height,line-height,font-size这三个属性,划分属性类的时候,尽量以步长为2的等差分布来定义,如:
.h0 , .h18 , .h20 , .h22 , .h24 , .h26 , .h28 , .h30 , .h32 , .h34 , .h36 , .h38 , .h40;
.lh18 , .lh20 , .lh22 , .lh24 , .lh26 , .lh28 , .lh30 , .lh32 , .lh34 , .lh36 , .lh38 , .lh40 ;
.f12 , .f14 , .f16 , .f18 , .f20 , .f22 , .f24 , .f26 , .f28 , .f30 ;
一来是为了保证这些小范围的的尺寸都能覆盖到;二是为了保证各个尺寸都是偶数,方便布局。
3)对于width,100以内的固定值,可以考虑以10为步长定义一个等差分布序列:
.w0 , .w10 , .w20 , .w30 , .w40 , .w50 , .w60 , .w70 , .w80 , .w90 , .w100
其它的固定值的属性类可以等到实际用到的时候再追加;
对于百分比的width,可以把10%到100%的值都定义出来,然后针对1/3 , 1/4 , 1/5, 1/6 也单独定义出来,因为这些都属于常用的一些布局比例,所以可以考虑提前定义。
4)margin跟padding的属性类有的可能是多余的,也有可能有缺少的,但是不能提前定义太多,可以考虑在实际用的过程中再追加;
5)border,background-color以及color可以考虑留下。由于这几个跟颜色有关,所以可以把设计图中最常用的几种颜色也抽出来分别定义追加进去,假如设计师比较有经验的话,做出来的东西就会比较规范,尤其是在通用的颜色这一块,不会搞出很多的颜色出来。在zxx.lib.css中,已定义的border,background-color还有color都是安全色,如果是设计师的颜色,其实也能定义成属性类,比如#7c7c7c,就可以定义成.c7c,bg7c,bd7c。
6)可以根据项目的实际情况增加border-radius以及flex等属性类。定义方式完全跟其它属性一样,如flex的:
.df { display: flex;}.dif { display: inline-flex;}.fdr { flex-direction: row;}.fdrr { flex-direction: row-reverse;}.fdc { flex-direction: column;}.fdcr { flex-direction: column-reverse;}.fwn { flex-wrap: nowrap;}.fww { flex-wrap: wrap;}.fwr { flex-wrap: wrap-reverse;}.jcfs { justify-content: flex-start;}.jcfe { justify-content: flex-end;}.jcc { justify-content: center;}.jcsb { justify-content: space-between;}.jcsa { justify-content: space-around;}.aifs { align-items: flex-start;}.aife { align-items: flex-end;}.aic { align-items: center;}.aib { align-items: baseline;}.ais { align-items: stretch;}.acfs { align-content: flex-start;}.acfe { align-content: flex-end;}.acc { align-content: center;}.acsb { align-content: space-between;}.acsa { align-content: space-around;}.acs { align-content: stretch;}.fxa { flex: auto;}.fxn { flex: none;}.fx1 { flex: 1;}.fx2 { flex: 2;}.fx3 { flex: 3;}.fx4 { flex: 4;}.fx5 { flex: 5;}.fx6 { flex: 6;}
以上这部分flex的属性类需结合auto-prefix这种工具来使用,因为有兼容性问题,需要统一添加前缀。
7)关于这些面向属性的css类组织:
第一,按照前面的这些建议,在张鑫旭的代码的基础上,调整成自己的项目所需之后,就应该把这个代码单独存放起来,作为一个像bootstrap这样的单独的库来使用;
第二,在实际工作过程中,如果要增加新的css属性类,只有在这个属性类有公用的价值,才能添加到第一步的公共属性类库里面去,否则的话,就只能在当前页面的main css里面去写。
8)最后,就是在坚持没有重复的代码前提下,根据实际情况去追加相关的css属性类。有公用性的就加到单独的库里,没公用性的话就追加到页面的main css里。
这些建议,也只是我个人的使用经验总结,有比较多的个人想法,如果觉得不对或者有疑问的话,欢迎私信或者评论交流。本文最重要的目的是分享这种面向属性的命名方法,我相信肯定会有朋友能够看到这个方法的价值的。
最后在张鑫旭的代码中,还有一部分代码,定义了很多简单常用的css类,比如浮动,浮动清除等等:
/* 块状元素水平居中 */.auto{margin-left:auto; margin-right:auto;}/* 清除浮动*/.fix{*zoom:1;}.fix:after{display:table; content:''; clear:both;}/* 基于display:table-cell的自适应布局 */.cell{display:table-cell; *display:inline-block; width:2000px; *width:auto;}/* 双栏自适应cell部分连续英文字符换行 */.cell2{overflow:hidden; _display:inline-block;}/* 单行文字溢出虚点显 示*/.ell{text-overflow:ellipsis; white-space:nowrap; overflow:hidden;}/* css3过渡动画效果 */.trans{ -webkit-transition:all .15s; transition:all .15s;}/* 大小不定元素垂直居中 */.dib_vm{display:inline-block; width:0; height:100%; vertical-align:middle;}/* 加载中背景图片 - 如果您使用该CSS小库,务必修改此图片地址 */.loading{background:url(about:blank) no-repeat center;}/* 无框文本框文本域 */.bd_none{border:0; outline:none;}/* 绝对定位隐藏 */.abs_out{position:absolute; left:-999em; top:-999em;}.abs_clip{position:absolute; clip:rect(0 0 0 0);}/* 按钮禁用 */.disabled{outline:0 none; cursor:default!important; opacity:.4; filer:alpha(opacity=40); -ms-pointer-events:none; pointer-events:none;}/*inline-block与float等宽列表*/.inline_box{font-size:1em; letter-spacing:-.25em; font-family:Arial;}.inline_two, .inline_three, .inline_four, .inline_five, .inline_six, .inline_any{display:inline-block; *display:inline; letter-spacing:0; vertical-align:top; *zoom:1;}.float_two, .float_three, .float_four, .float_five, .float_six{float:left;}.inline_two, .float_two{width:50%; *width:49.9%;}.inline_three, .float_three{width:33.33333%; *width:33.3%;}.inline_four, .float_four{width:25%; *width:24.9%;}.inline_five, .float_five{width:20%; *width:19.9%;}.inline_six, .float_six{width:16.66666%; *width:16.6%;}.inline_fix{display:inline-block; width:100%; height:0; overflow:hidden;}
这些代码也可以根据实际情况稍微调整,大部分在实际工作中都有使用到的场景,这里仅作引用介绍,因为使用起来也很简单的。
为了说明这个命名方法的作用,我做了一个demo,以博客园博客列表页来说明如何应用这种命名方法。先来看博客列表页的结构:
可以看到这个列表页其实是用到了很多语义化的命名的css类的,假如要用面向属性的命名方法来定义,就会变成下面这个样子:
实际效果如下:
相关源码可访问以下链接查看:
http://liuyunzhuge.github.io/blog/css_name/html/demo.html
在以上这个实践过程中,并没有所有的样式都使用这种命名方法,主要的原因是单纯的属性命名对无法对伪类或者伪元素进行很好的样式控制,而正好博客园列表页中的每个链接的样式都不一样,所以没办法统一。这也正是面向属性的命名方法的一种限制情形。
虽然本文的目的是在推崇这种面向属性的命名方法,但是你从文中的内容也可以看到,这种方法是有针对性的,前面一开始介绍的时候就说过:
采用面向属性的css命名,把那些我们实在想不出名字的,而且没有必要起名字的,并且可以只用单一的css属性或者组合的单一css属性来描述的部分,通通都用面向属性的css类来控制样式
使用这个方法的最大前提就是没有必要起名字,并且能够用组合的单一属性来描述的内容,就可以这种方法来加速页面布局的工作。像前面应用实践介绍过的那种情形一样,当你需要配合伪类,伪元素或者背景图片的时候,就不太适合用这种方法。
在面向属性的命名方法不能使用的时候,有另外两种css的命名或者说组织方式可以使用:
1)语义化的命名,在整个页面,语义化的css命名还是不可获缺的一部分,尤其是那些划分页面模块的,比如.header .footer .logo等等,抽象公共样式或者公共组件的,如.dropdown,.btn,.tab等等。这些是css模块化,代码重用的比较大的组织单位,如果把它们也拆分了,会使得整个站点的css结构非常的复杂,那样的话还不如直接用style呢;
2)采用层级来命名,而且要多用直接子元素选择器,虽然在张鑫旭的博客中不建议css有层级,但是有的时候如果不想命名,又无法用面向属性的命名方法来解决的设计,可以考虑用层级来解决,在bootstrap的源码中很多的css组件,比如nav,dropdown,tab等等,都是通过层级来控制的,一来是起到命名空间的作用,二来是减少对层数较深的子孙元素产生影响。但是层数也不能太深,最好不要超过3层,否则html结构变化之后,就会影响css代码的结构。
本文主要是传播面向属性的css命名方法这种思想,由于它在我实际工作中帮助我减少了很多不必要的css命名,所以我专门写了这篇文章把它分享出来。这个里面也包含了很多自己在工作中产生的想法,不一定符合你自己的实际需求,要是感兴趣的话,可以去研究下张鑫旭的那2篇文章,相信你自己也能够总结出一些属于自己的东西。感谢阅读:)