把你的CSS组织成逻辑结构
Jonathan Snook在他写的一本很棒的书SMACSS中,提出可以把你的css分为4个不同的类别来组织,他们是基础样式,布局样式,模块样式以及状态样式。基础样式是由重置元素规则和元素默认样式组成。布局样式是定位站内元素以及通用布局就像网格系统。模块样式是可以复用的视觉元素,状态样式就是通过javascript涉及到开启或关闭。
在SMACSS体系中,模块(如同我说的组件)在所有的css规则中占据绝大多数,所以我时常认为有必要把他们进一步分解为抽象的模版。
组件是可以独立的视觉元素。模版从另一方面来说是由块组成的。模版本身不能独立应用而且很少描述外观和感觉。相反,他们可以是单一的,可重复的模式,放在一起形成一个组件。
提供一个具体的例子,一个组件可能是一个模态对话框。这个对话框可能头部是站内通用的渐变背景色。并且周围有阴影,右上角也许有一个关闭按钮,他估计是水平垂直居中定位的。这四个模式中的每一个在全站中可能一次次的使用,所以每次你都不必重新编写他们的模式。因为他们全是模版,可以一起组成一个模态对话框组件。
通常我在html中不使用模板类,除非我有个很好的理由。相反,我在组件定义中使用一个包含模版样式的预处理器。下面我将详细讲解这些以及我这么做的合理性。
只用类名作为样式而且只做样式
任何参与过大型项目的人都会遇到一个问题,就是一个html元素有个完全不知道干什么用的类名。你想要删除他,但是你犹豫了,因为他可能有一些你不知道的用途。这样的情况一次次的发生,久而久之,你的html充满了各种不知任何用途类名,只因为团队成员害怕删除它们。
问题是类名在前端开发中通常赋予了太多的责任。他们用来定义html元素样式,作为javascript的钩子,添加到html中用作功能检测和自动化测试等等。
当一个类名在应用程序中的太多地方使用,这是一个问题。那么把她从html中删除就成了一个非常可怕的事情。
然而,随着建立一个约定,这个问题是可以完全避免的。当你在一个html中看到一个类名,应该立刻明白他是用来什么的。我的建议是给所有不用于定义样式的类名加一个前缀。我使用.js作为javascript的钩子,用.supports作为Modernizr 类名。所有仅仅是定义样式的类名没有前缀。
这使得发现没用的类名并删除它们如同在样式表目录搜索一样容易。你甚至可以用javascript将这个过程自动化,通过检查在html中的类名是不是在document.styleSheets对象里面来判断。如果不在document.styleSheets里面的类名,就可以安全的删除了。
一般来说,分离你的内容与你的表现是一个最佳实践,同样重要的还有将你的表现与功能分离。使用定义样式的类名作为javascript钩子,会把你的css与javascript紧紧绑在一起,在某种程度上,在不破坏功能的前提下,更新一些元素的外观是很难的或是不可能的。
命名有逻辑结构的类名
如今很多人写css用连字符作为词的分隔符。但是仅仅连字符是不足以区分不同种类的类名的。
Nicolas Gallagher最近写的关于这个问题的解决方案。我也采用了(只需要轻微的修改)并取得了很大的成功。为了说明需要有如下命名的约定。
/* A component */
.button-group { }
/* A component modifier (modifying .button) */
.button-primary { }
/* A component sub-object (lives within .button) */
.button-icon { }
/* Is this a component class or a layout class? */
.header { }
从上面的类名,不可能知道他们要应用什么类型的规则。这不仅增加开发过程中困扰,也加大用自动化方式测试css和html的难度。一个结构化命名约定可以让你看到一个类名,就准确的知道跟他有关的其他类名,以及他应该出现在html中的哪些地方。命名和测试变得容易可行,这在以前是不可能的。
/* Templates Rules (using Sass placeholders) */
%template-name
%template-name--modifier-name
%template-name__sub-object
%template-name__sub-object--modifier-name
/* Component Rules */
.component-name
.component-name--modifier-name
.component-name__sub-object
.component-name__sub-object--modifier-name
/* Layout Rules */
.l-layout-method
.grid
/* State Rules */
.is-state-type
/* Non-styled JavaScript Hooks */
.js-action-name
重做第一个例子
/* A component */
.button-group { }
/* A component modifier (modifying .button) */
.button--primary { }
/* A component sub-object (lives within .button) */
.button__icon { }
/* A layout class */
.l-header { }
工具
维护一个有效的并且有序的css架构是很困难的,尤其是在一个大型项目中。这里那里一点不好的规则可以像滚雪球一样变成一个难以控制的混乱局面。一旦你的应用程序中的css进入一个特殊领域和有!important王牌的混战中,他几乎不可能重新开始去恢复。关键是从一开始就避免这些问题。
幸运的是,这里有一些工具可以容易的控制你的网站的css架构。
预处理程序
如今谈到css工具就不可避免的要提及预处理程序。所以本文也不例外。但是在我赞美他们的用处之前,我应该说一些他的注意事项。
预处理程序可以帮你更快但不是更好的书写css。最后演变成纯粹的css和同一个规则的应用。如果一个预处理程序可以让你写css更快,那么也可以让你更快的写出糟糕的css。所以在让预处理程序解决你的问题之前,应该理解一下好的css架构就显得尤为重要。
许多所谓的预处理程序的特性实际上对于css架构是很糟糕的。以下是一些我不惜一切代价避免的特性(尽管这些普遍思想应用于所有的预处理程序语言,这些准则特别是在sass中应用)。
1.代码组织上从不嵌套规则。当输出的css是你想要的
2.如果你不传参数的话从不使用混入类。没有参数的混入类可以更好的作为模版使用,这样的模版可以扩展
3.从不在选择器上使用@extend,他不是一个单独的类。从设计角度他没有意义,而且会加大css编译后的体积
4.从不使用@extend为ui组件在组件修改器,因为你丢失了继承链(这一点)
预处理程序中最好的部分是像@extend和 %placeholder的函数。这两者容许你轻松管理抽象出来的css,而没有增加你的css,或是在html中添加一大串很难管理的基础类名.
因为有时你想在你的html中使用这些类名@extend应该小心使用。例如,当你第一次了解@extend,你可能会在你的所有的修改类里面尝试的使用它,就像这样:
.button {
/* button styles */
}
/* Bad */
.button--primary {
@extend .button;
/* modification styles */
}
这样做的问题是在html中你丢失了继承链。现在用javascript很难选中所有的按钮实例。
作为一般的规则,我从来不扩展ui组件,或是以后我可能想要了解的类型。这是从另一个方面帮助区别模版与组件的方式。模版是一些在你的应用程序逻辑中不需要考虑的目标,所以可以安全的用预处理程序扩展。
下面是他如何使用上面模态例子引用
.modal {
@extend %dialog;
@extend %drop-shadow;
@extend %statically-centered;
/* other modal styles */
}
.modal__close {
@extend %dialog__close;
/* other close button styles */
}
.modal__header {
@extend %background-gradient;
/* other modal header styles */
}
CSS Lint
Nicole Sullivan 和 Nicholas Zakas 创建了 CSS Lint 作为一个工具,用来帮助开发人员在他们的css中检查不好的实践。他们的网站上这样描述:
Css lint指出你的css代码的问题。他有基础语法检查,也应用一组代码规则,查找问题模式或低效率迹象。规则都是可以插入的,所以你可以容易的书写自己的或是省略你不想要的规则
尽管通用规则可能不适用大多数项目,css lint最好的特性是它可以定制成你想要的。这意味着你可以从他们默认的列表里选择你想要的样式,也可以编写自己的样式。
对于任何一个大型团队像css lint这样的工具是必要的,用以确保基本的一致性和符合约定。就像我前面暗示的一样,一个约定的主要原因是像css lint这样的工具所以可以容易的识别破坏他们的规则。
基于上面我提出的约定,书写特定的反模式规则变得很容易。这里有一些我使用的建议