有时候,你会创建一个视觉组件,离网站某部分左边、上边均20px:
.widget { position: absolute; top: 20px; left: 20px; background-color: red; font-size: 1.5em; text-transform: uppercase; }然后在后面,你需要在不同位置使用这一组件。上述 CSS 代码将无法正常使用,因为它在不同上下文中无法复用。
问题就在于,你让这个选择器做太多事了。在一条规则里,你定义了外观和风格,还定义了布局和位置。外观和风格是可重复使用的,但布局和位置不是。因为它们是一起用的,整个规则就像个妥协结果。
虽然第一眼看上去,这可能无害,但它通常会导致 CSS 不熟练的开发者不断复制粘贴。如果一个团队新成员想要某样东西,它看起来像某一组件,比如说 .infobox,他们可能会先尝试该类。但是,那行不通的,因为新 infobox 会以不一样的方式定位,你觉得他们接下来可能做什么?在我的经验中,大多数新开发人员都不会把规则破开,整理成可复用的部分。相反,他们会创建一个新选择器,把这一特定实例需要的代码复制、粘贴进来,结果就不必要地重复了代码。
原因所有上述问题都有一个共性,他们把过多样式化的责任交给了 CSS。
这说法看似奇怪。毕竟,它是一个样式表,它难道不该承担大部分(如果不是全部)的样式化工作?这不正是我们想要的吗?
对这个问题的简单答案是“是的”,但是,像往常一样,事情并不总是那么简单。内容与表现分离是件好事,但就因为你从 HTML 中分离出 CSS,并不意味着你的内容就和表现分离了。换句话说,从 HTML 代码中分拆出所有表现代码并没有实现我们的目标,因为我们的 CSS 要正常工作,需要非常了解 HTML 结构。
此外,HTML 很少只是内容,它几乎总也是结构。而往往这种结构包含一些容器元素,它们没有其他目的,仅仅是让 CSS 隔离出一组特定元素。即使没有表现的类,这仍明显是表现与HTML的混合。但是否有必要把表现混入内容?
我相信,基于 HTML 和 CSS 的当前状况,让它们共同努力一同担当表现层的工作是必要且明智的。内容层仍然可以通过模板和 partials 抽象出。
解决方法既然你的 HTML 和 CSS 要一同合作来搭建 web 应用程序表示层,他们就需要一个方法,可以实践所有良好的 CSS 架构原则。
我发现的最好办法是,CSS 里尽可能少包括 HTML 结构。CSS 应该定义一组视觉元素的外观(以减少与 HTML 的耦合度),这些元素不论出现在 HTML 的哪里,都应该是那个样子。如果某个组件在不同情况下需要不同外观,它应该被定义成其他东西,应用它则是 HTML 的责任。
举个例子, CSS 可能通过.button 类定义一个按钮组件。如果 HTML 想要一个特定元素,看起来像个按钮,它就应该使用这个类。如果有些情况需要不同的按钮(比如大点的,或是全宽度的),那么 CSS 需要一个新类来定义外观,然后,HTML 应用新类,来布置新外观。
CSS 定义你的组件是什么样子,HTML 则将这些样式配给页面元素。CSS 需要了解的 HTML 结构越少,结果会越好。
在 HTML 中明确声明你的需要,会有一个很大的好处,它可以让其他查看标签的开发人员确切知道什么元素应该是什么样子。意图是显而易见的。如果不是这种做法,就很难区分元素的外观是有意还是无意的,这给团队带来困惑。
一种常见的反对声音是说,标签中加入许多类会导致有很多工作要做。一条 CSS 规则可以针对特定组件的一千个实例,那么有什么值得我们为了在标签中明确声明去书写一千次的类名?
虽然这种担忧是有的,它却可能会误导。它的言下之意是,或者你在 CSS 中使用父选择器,或者你手写1000次 HTML 类,但我们显然有其他替代方法。Rails 或者其他框架中的视图层的抽象,可以在保持明确声明外观的同时无需在 HTML 里重复写同一类。
最佳实践在我犯过一遍又一遍的上述错误,并付出代价后,我得出了以下建议。虽然并不全面,但我的经验表明,坚持这些原则将有助你更好地实现良好 CSS 架构目标。
目的明确要确保你的选择器不会误操作其它元素的最好方法,是不给它们机会。#main-nav ul li ul li div 这样的选择器,很容易就会在你更改标签过程中误样式化其他元素。而 .subnav 这样的样式,绝不会无意样式化意外元素。给你想要样式化的元素应用类是最好的方式,这样可以保持你的 CSS 可预见。
/* Grenade */ #main-nav ul li ul { } /* Sniper Rifle */ .subnav { }以上两个例子,你可以想像第一个是手榴弹,第二个则是狙击枪。手榴弹今天可能工作得很好,但你永远不知道,什么时候无辜平民会进入到爆炸范围内。
分割你的需要我已经提到过,一个组织良好的组件层可以帮助离散 CSS 与 HTML 结构。此外,你的 CSS 组件本身应该是模块化的。组件应该知道如何样式化自己,并且能把样式化工作做好,但它们不应该负责它们的布局或位置,也不应该对他们将如何与周围元素间隔开这种事做太多假设。
总的来说,组件应该定义它们的外观,而不是布局或位置。当你看到位置,宽度,高度和边距与背景,颜色,字体等属性出现在同一规则里时,就要小心了。
布局和位置,应该由一个独立的布局类或单独的容器元素处理。(请记住,要有效分离内容与表现,往往要求把内容从容器中分开。)
命名空间你的类我们已经研究了为什么父选择器在封装和防止样式交叉污染时不是100%有效的。一个更好的方法是应用命名空间到类上。如果一个元素是视觉组件一员,则每个子元素类都应该使用组件基类名称作为命名空间。
/* High risk of style cross-contamination */ .widget { } .widget .title { } /* Low risk of style cross-contamination */ .widget { } .widget-title { }命名空间你的类,可以让你的组件自包含、模块化。它最大限度地减少现有类发生冲突的可能性,并降低样式化子元素需要的特殊性。
通过修饰类来扩展组件当现有的组件在不同上下文中略有不同时,创建一个修饰类来扩展它。
/* Bad */ .widget { } #sidebar .widget { } /* Good */ .widget { } .widget-sidebar { }