很幸运的是,已经有一项叫做CSS的技术,就是特意设计用来解决这个问题的。使用CSS,我就可以在HTML组件之间到处传递样式,从而以最小的代价来保证一致性的设计。这很大程度上要感谢两个CSS特性:
继承
层叠(CSS当中的C,cascade)
尽管这些特性让我们能够以一种DRY且有效率的方式来给Web文档添加样式,同时也是CSS存在的原因,但很明显,它们已经不再受到青睐。在一些CSS方法论里,如BEM和AtomicCSS这些通过程序化封装CSS模块的方法,许多都尽力去规避或者抑制这些特性。这也让开发者有了更多机会去控制他们的CSS,但这仅仅是一种基于频繁干预的专项控制。
我准备带着对模块化界面设计的尊敬在此重新审视继承、层叠和作用域。我想要的告诉你的是如何利用这些特性让你的CSS代码更简洁,实现更好的自适应,并且提高页面的可扩展性。
继承和font-family尽管许多人在抱怨CSS为什么不单单提供一个全局作用域,但如果它这么做的话,那么就会有很多重复样式了。反之,CSS有全局作用域和局部作用域。就像在JavaScript里,局部作用域有权限访问父级和全局作用域,而在CSS里,局部作用域则帮助了继承。
例如,如果给根部(也作:全局)的html元素定义一个font-family属性,那么可以确定这条规则会在文档里应用到所有祖先元素(有一些例外情况,将在下个部分讨论)。
html{font-family:sans-serif;}/*Thisruleisnotneeded?p{font-family:sans-serif;}*/
就像在JavaScript里那样,如果我在局部作用域里定义了某些规则,那么它们在全局,或者说在任意祖先级的作用域中都是无效的,只有在它们自己的子作用域里是有效的(就像在上面代码中的p元素里)。在下个例子当中,1.5的line-height并没有被html元素用上。但是,p里的a元素则运用上了line-height的值。
html{font-family:sans-serif;}p{line-height:1.5;}/*Thisruleisnotneeded?pa{line-height:1.5;}*/
继承最大的好处就是你可以用很少量的代码为一致性的可视化设计建立一个基础。而且这些样式甚至将作用到你还没写的HTML上。我们在讨论不会过时的代码!
替代方法
当然有另外一种方式提供公用样式。比如,我可以创建一个.sans-serif类…
.sans-serif{font-family:sans-serif;}
…并将它应用到任意我想要它有这个样式的元素上去:
pclass="sans-serif"Loremipsum./p
这种方法提供了一些控制上的权利:我可以准确地挑选决定哪些元素应用这个样式,哪些元素不用。
任何能够控制的机会都是很吸引人的,但有一些明显的问题。我不仅需要手动地给需要应用样式的元素添加类名(这也意味着我要首先确定这个样式类是什么效果),而且在这种情况下也已经有效地放弃了支持动态内容的可能性:不管是富文本编辑器还是Markdown解析器都没办法给任意的p元素提供sans-serif类。
class="sans-serif"和style="font-family:sans-serif"的用法差不多–除了前者意味着要同时在样式表和HTML当中添加代码。使用继承,我们就可以在其中一个少写点,而另外一个则不用再写了。相比给每个字体样式写一个类,我们可以只在一个声明里,给html元素添加想要的规则。
html{font-size:%;font-family:sans-serif;line-height:1.5;color:#;}inherit关键字
某些类型的属性是不会默认继承的,而某些元素则不会继承某些属性。但是在某些情况下,可以使用[propertyname]:inherit来强制继承。
举个例子,input元素在之前的例子中不会继承任何字体的属性,textarea也一样不会继承。为了确保所有元素都可以从全局作用域中继承这些属性,可以使用通配选择符和inherit关键字。这样,就可以最大程度地使用继承了。
*{font-family:inherit;line-height:inherit;color:inherit;}html{font-size:%;font-family:sans-serif;line-height:1.5;color:#;}
注意到我忽略了font-size。我不想直接继承font-size的原因是,它会将heading元素(译者注:如h1)、small元素以及其他一些元素的默认user-agent样式给覆盖掉。这么做我就可以节省一行代码,并且让user-agent决定想要什么样式。
另外一个我不想继承的属性是font-style:我不想重设em的斜体,然后再次添加上它。这将成为无谓的工作并会产生多余的代码。
现在,所有不管是可以继承或者是强制继承的字体样式都是我所期望的。我们已经花了很长时间只用两个声明区块来传递一个一致性的理念和作用域。从现在开始,除开一些例外情况,没有人会在构造组件的时候还需要去考虑font-family、line-height或者color了。这就是层叠的由来。
基于例外的样式我可能想要主要的heading元素(h1)采用相同的font-family、color和line-height。使用继承就是很好的解决方案,但是我又想要它的font-size不一样。因为默认的user-agent样式已经给h1元素提供了一个大号的font-size(但这时它就会被我设置的相对基础字体大小为%的样式覆盖掉),可能的话我不需要这里发生覆盖。
然而,难道我需要调整所有元素的字体大小吗?这时我就利用了全局作用域的优势,在局部作用域里只调整我需要调整的地方。
*{font-family:inherit;line-height:inherit;color:inherit;}html{font-size:%;font-family:sans-serif;line-height:1.5;color:#;}h1{font-size:3rem;}
如果CSS元素的样式默认被封装,那么下面的情况就不可能了:需要明确地给h1添加所有字体样式。反而,我可以将样式分为几个单独的样式类,然后通过空格分隔来逐一给h1添加样式:
h1class="Ff(sans)Fs(3)Lh(1point5)C(darkGrey)"HelloWorld/h1
不管哪种方式,都需要更多的工作,而且最终目的都是一个具备样式的h1。使用层叠,我已经给大部分元素赋上了想要的样式,并且只在一个方面使得h1成为一个例外。层叠作为一个过滤器,意味着样式只在添加新样式覆盖的时候才会发生改变。
元素样式我们已经开了个好头,但想要真正地掌握层叠,还需要尽可能多地给公共元素添加样式。为什么?因为我们的混合组件是由独立的HTML元素构成,并且一个屏幕阅读器友好的界面充分利用了语义化结构标记。
换句话说,让你的界面“分子化”(使用了atomic设计术语)的“atoms”样式应该在很大程度上可定位并且使用元素选择符。元素选择符的优先级很低,所以它们不会覆盖你之后可能加进来的基于类的样式。
首先应该做的事情就是给所有你即将需要使用的元素添加样式:
a{…}p{…}h1,h2,h3{…}input,textarea{…}/*etc*/
如果你想在无冗余的情况下有个一致性界面的话,那么下一步非常重要:每当你创建一个新组件的时候,如果它采用了一些新元素,那么就用元素选择符来给它们添加样式。现在不是时候去使用限制性、高优先级的选择符,也没有任何需要去编写一个样式类。语义化元素就使用其本身。
举个例子,如果我还没有给button元素(就像前一个例子)添加样式,并且新组件加入了一个button元素,那么这就是一个给整个界面的button元素添加样式的好机会。
button{padding:0.75em;background:#;color:#fff;}button:focus{outline:0.25emsolid#dd0;}
现在,当你想要再写一个新组件并且同样加入按钮的时候,就少了一件需要操心的事情了。在不同的命名空间下,不要去重写相同的CSS,并且也没有类名需要记住或编写。CSS本就应该总是致力于让事情变得简单和高效–它本身就是为此而设计的。
使用元素选择符有三个主要的优势:
生成的HTML更加简洁(没有多余的各种样式类)。
生成的样式表更加简洁(样式在组件间共享,不需要在每个组件里重写)。
生成的添加好样式的界面基于语义化HTML。
使用类来专门提供样式常常被定义为“北京最权威白癜风专科白癜风初期治疗方法