12345678.widget { position: absolute; top: 20px; left: 20px; background-color: red; font-size: 1.5em; text-transform: uppercase; }
下面,你需要在网站的其他区域使用该组件,那么上面的这个代码明显是错误的,不可重用的。
问题的关键是你让.widget这个选择器做的事情太多,不仅对该组件的位置进行了规定,还对它的外观和感觉方面进行了样式。外观和感觉可以通用,而位置是不可以的。有时候,把它们整合起来使用反而会大打折扣。
虽然这些看起来并无害处,对一些缺乏经验的CSS程序员来说,复制和粘贴已经成为一种习惯。如果一个新团队需要一个特定组件,比如.infobox,他们会尝试使用这个类选择器。但如果该信息框没有按照期望的那样,在每个需要的地方正确显示出来。这时,你认为他们会怎么做?以我的经验来看,他们会打破可重用这一规则,相反,他们会简单地把这些代码复制粘贴到每个需要的地方。做些不必要的重复工作。
3.原因
上面列举的这些常规错误实践都有一个相似性,CSS样式承担过多。
对这样的说法你会感到奇怪,毕竟,它是一个样式表,难道不应该承担大多数(如果不是全部)的样式吗?那不正是我们想要的吗?
的确。但是通常来讲,事情并没有那么简单。内容与表现(presentation)相分离是件好事,但CSS从HTML中独立出来并不意味着内容也需要从表现中分离。换句话说,如果CSS请求深入分析HTML架构,那么从HTML中分拆所有的显示代码并不一定会实现所有的目标。
此外,HTML很少会只包含内容,也表示整体框架。通常,架构是会包含container元素,允许CSS隔离一些固定元素。即使没有表象类(presentational classes),也能混合HTML清晰地把内容展示出来。
我相信,鉴于当前的HTML和CSS状态,把HTML和CSS明智地结合起来,当做表现层是非常需要的。而通过模板和局部模板(partials)也可以把内容层进行分离。
4.解决方案。
如果把HTML和CSS结合起来,作为一个Web应用程序的表现层,那么它们需要采取一些方式更好地促进优秀CSS架构的形成。
最好的方法是CSS中尽可能少的包含HTML架构。CSS则是应该定义元素的视觉效果,无论该视觉元素在哪里。如果有一些特定的组件需要在不同的场合显示不同的效果,那么应该赋予不同的名称。例如,CSS通过.button类选择器定义了一个按钮组件。如果HTML想要一个特定的元素看起来像按钮,那么就可以使用.button。如果这里有特殊要求,这里的按钮与其他的有所不同(有可能更大和宽些),那么CSS需要定义一个新的类,HTML可以使用新的类来赋予该元素新的视觉效果。
CSS赋予元素的外在特征,HTML在页面上进行调用。更少的CSS能被更多的HTML架构调用是最好的。
准确地在HTML中声明元素不仅可以清晰表达设计意图,其他开发者也可以清晰地查看标记并且知道元素将呈现的样子。如果没有这种实践,它很难区分一个元素的外观设置是有意或无意的,这样很容易导致团队混乱。
在标记中填入大量的类(classes)是种常见缺陷,这样做往往需要花费额外的精力。一个CSS样式可以给一个特定组件引用上千次。那么,为了在标记里面进行显示声明,就真的值得去重复编写这样的类吗?
虽然这种担心是有效的,但它可能会产生误导。言下之意就是无论你在CSS中使用一个父选择器还是亲手编写上千个Class,这里都会有些额外的选择。在Rails或者其他框架里查看同级别抽象很大程度上可以在HTML中保持很好的视觉外观,并且无需在类中一遍又一遍地编写相同的类。
5.最佳实践。
针对上面的种种错误,我进行了很好地总结,并且根据自身经验提出了一些建议,希望它们能帮助您更好地实现良好的CSS架构目标。
专注
确保选择器对一些元素不进行无关样式的最好方法是不给它们机会。例如像#main-nav ul li ul li div这样的选择器可能很容易地应用于不想要的元素上。另一方面,像.subnav这样的选择器就不会给它们任何机会。把类选择器直接应用于你想要的元素上是最好的方式,并且可以保持元素的可预测性。
12345/* Grenade */#main-nav ul li ul { } /* Sniper Rifle */.subnav { }
模块化
一个组织结构良好的组件层可以帮助解决HTML架构与CSS那种松散的耦合性。此外,CSS组件本身应该是模块化的。组件应该知道如何进行样式和更好地工作,但是关于布局、定位以及它们与周围元素的关系不应该做太多的假设。
一般而言,CSS要定义的应该是组件的外观,而不是布局或者位置。同样在使用background、color和font这些属性时也要遵循原则使用。
布局和位置应当由一个单独的布局类或者单独的容器元素构成(请记住,有效地把内容与展示进行分离其实就是把内容与容器进行分离)。
给类进行命名空间
我们已经检查出为什么父选择器不能在封闭和防止交叉样式污染上面发挥100%的功效。而一个更好的解决方案就是在类上应用命名空间。如果一个元素是可视化组件的一员,那么该元素的每个子元素都应该使用基于命名空间的组件。
1234567/* High risk of style cross-contamination */.widget { } .widget .title { } /* Low risk of style cross-contamination */.widget { } .widget-title { }
给类进行命名空间可以保持组件独立性和模块化。它可以把现有类冲突降至最小并且减少子元素的一些特殊要求。
创建修饰符类来扩展组件
当一个现有组件需要在一个特定的语境中有所不同时,可以创建一个修饰符类(modifier class)来扩展它。
1234567/* Bad */.widget { } #sidebar .widget { } /* Good */.widget { } .widget-sidebar { }