6.9 可扩展模块化架构CSS代码规范(2)——SMACSS(II)
Module :(为了区别于Drupal的模块,也称之为component)
相对于页面中大块的Layout,Module是页面中的小组件,它可以是Logo,导航条,也可以是carousels(旋转木马,我从来都不知道这些东西的中文叫什么,请大家不吝赐教),Module一般都位于Layout里面,有的时候,Module里面还可以包含另一个Module。
关于Module,最核心的原则在于:Module应该是各自独立的组件,无论你把它放在页面的哪个位置,样式和布局都不应该被破坏。这样做的好处,就是让Module成为可以重复使用的组件。
在为Module定义样式规则的时候,应该避免使用ID和元素选择器,只能使用类选择器。Module的类选择器不带特定前缀。如果一个元素的内部有一系列的DOM节点,那么最好使用后代和子选择器如:
.menu { /* Some CSS properties */ } .menu > h2 { /* Some CSS properties */ } .menu li { /* Some CSS properties */ }
在这个例子中,为了定义menu中的h2使用了子元素选择器,这样做,并会不影响到其它元素中的h2,只有当h2作为menu的子元素出现时,规则才会被启用。基于同样的原因,第三条规则.menu li 就不够好,因为,.menu中出现的每一个li都被影响了。为了避免类似问题,应该改用以下方式:
.menu { /* Some CSS properties */ } .menu-header { /* Some CSS properties */ } .menu-item { /* Some CSS properties */ }
在这个例子中,通过指定的Class,我们可以精确的控制我们需要控制的元素。同时,你应该已经发现,在module内部的元素,都使用module名作为前缀,这是SMACSS推荐的方式。这样做的好处一方面是可以很直观的通过类名知道某个元素属于哪个module,另一方面,单一的选择器可以保证CSS规则只作用于某个特定的元素,而不会影响到其它元素。
关于子模块(Subclassing Modules ):
当我们在页面的不同区域,遇到相同Module的时候,我们的本能反应是通过上级元素重新定义这个Module。如下:
.pod { width: 100%; } .pod input[type=text] { width: 50%; } #sidebar .pod input[type=text] { width: 100%; }
这样做的麻烦在于,你必须使用更多的选择器才能在样式表中清楚的定义你想控制的元素,或者,你就不得不使用!important。
继续来看我们的例子“pod”,input有两个不同的宽度。整个页面,input应该占据一半的宽度。但是在sidebar中,input被调整为占据sidebar宽度的全部(100%)。现在,我们需要在页面上添加一个新的组件,它和.pod的属性大致一样,因此,我们决定重复利用这个类。但是,这个新组件有一个特点,那就是无论它在页面的哪个位置出现,它的input都是固定的180像素宽。于是,代码如下:
.pod { width: 100%; } .pod input[type=text] { width: 50%; } #sidebar .pod input[type=text] { width: 100%; } .pod-callout { width: 200px; } #sidebar .pod-callout input[type=text], .pod-callout input[type=text] { width: 180px; }
这里我们利用#sidebar重写了两次input的值。
我们应该做的,其实是将sidebar中具有指定宽度的pod作为一个子类,并为其指定相应的样式。如下:
.pod { width: 100%; } .pod input[type=text] { width: 50%; } .pod-constrained input[type=text] { width: 100%; } .pod-callout { width: 200px; } .pod-callout input[type=text] { width: 180px; }
上面具有.pod-前缀的都是pod的子元素。这样,基module和子module的类名都可以被用在Html中。如下:
<div class="pod pod-constrained">...</div> <div class="pod pod-callout">...</div>
如果某个module在页面或者网站的另一个位置出现时外观需要有一些变化,你就应该使用这种子类的方式(sub-class)。
如果IE6不在考虑范围之内的话,你可以直接采用下面的方式:
.pod.pod-callout { } <!-- In the HTML --> <div class="pod pod-callout"> ... </div>
State:
State是样式的特定状态。一般来说被应用于一个Layout或者一个Module上,指定它的某种状态。比如一条消息,可以是一个错误的(状态),或者一个搜索栏处于被隐藏的状态。State样式使用is-作为前缀,举例如下:
<div id="header" class="is-collapsed"> <form> <div class="msg is-error"> There is an error! </div> <label for="searchbox" class="is-hidden">Search</label> <input type="search" id="searchbox"> </form> </div>
这里header只有一个ID,它本身是一个Layout,is-collapsed是它的一个状态(表示收缩)。你可以推测,当没有这个is-collapsed的时候,header的状态就是默认的“expanded”(即展开)。msg是一个Module,其状态为is-error的错误状态。相对的,当其状态为正确或成功时,则可以是is-success。最后,搜索栏处于被隐藏的状态,is-hidden,而这条State则是被应用在Base元素上的。
State Rules对于base rule、layout rule、module rule只是一个叠加,并没有重写。
sub-module 和state有一些相像,他们都会修改一个已有的元素。但是他们有两点区别:
State可以应用于layou 和/或者 module
state主要用于和Javascript配合使用。
其中,第二点尤其重要。Sub-module在页面渲染时作用于元素,然后就不再发生变化。State styles会在一个元素发生变化的时候起作用。
State一般都使用单一选择器。并且由于需要覆写样式,因此!important是被允许甚至推荐使用的。大部分的情况下,你也不会需要为某个元素指定一个以上的状态。
Theme:(为了区别于Drupal的主题,也称之为skin)
Theme可以覆写上面的几类。为你的网站设立不同的主题(again,这里说的不是Drupal主题),比如平时网站是蓝色系的,圣诞节的时候则改为红色系的,这样就需要改变页面上的很多元素属性,如链接的颜色,元素的边框,甚至页面的布局等。此类规则使用.theme-作为前缀,以示区别。