现代 CSS

挑战最佳CSS实战

特别声明:如果您喜欢小站的内容,可以点击申请会员进行全站阅读。如果您对付费阅读有任何建议或想法,欢迎发送邮件至: airenliao@gmail.com!或添加QQ:874472854(^_^)

本文由大漠根据的《Challenging CSS Best Practices》所译,整个译文带有我们自己的理解与思想,如果译得不好或不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:http://coding.smashingmagazine.com/2013/10/21/challenging-css-best-practices-atomic-approach/,以及作者相关信息

——作者:

——译者:大漠

这篇文章介绍的技术特性在Yahoo通过实践以及有问题的技术代码到今天为止我们还在使用。你可能有兴趣阅读Jonathan Snook的《Decoupling HTML From CSS》、Tim Huegdon的《On HTML Elements Identifiers》和Robin Rendle的《Atomic Design With Sass》等等。请记住:一些提到的技术并不认为是最佳实践。——Thierry Koblentz

当谈到CSS,我相信神圣的原则——点分离(SoC)。他已经让我们接受膨胀、报废、冗余、缓存甚至更多。现在,我确信唯一的方法是远离这一原则来改善我们的作者样式表。

对于从未听说过SoC原则在Web设计中的上下文,它涉及到一些俗称“三层分离”:

  • 结构
  • 表现
  • 行为

为了避免一些担忧,将它划分成独立的资源:一个HTML文档,一个或多个样式文件和一个或多个JavaSctript文件。

但当涉及到表现层,“最佳实践”已经超出了资源的分离。CSS作者完全可能通过样式表应付,例如Dave Shea写的CSS禅意花园就是一个优秀的项目。CSS禅意花园是大多数(如果不是全部)开发人员用来做为如何写样式表的参考标准。

标准

今天能过实践来帮我说明相关问题,我将使用一个非常常见的模块:媒体对象。它结合了HTML标记和CSS样式,将是我们开始探讨的起点。

模板结构

在我们的模板中,有一个容器(div.media)包含了一个链接容器(a.img),接着有一个div容器(div.bd):

<div class="media">
    <a href="http://twitter.com/thierrykoblentz" class="img">
        <img src="thierry.jpg" alt="me" width="40" />
    </a>
    <div class="bd">
        @thierrykoblentz 14 minutes ago
    </div>
</div>

CSS

我们给容器设置10px的margin,同时给容器和div.bd设置为一个BFC。换句话说,容器包含了一个浮动的链接,并且div.bd不能围绕在链接的周围。同时在图片和文本之间设置一个10像素的margin(在浮动的一侧):

.media {
    margin: 10px;
}
.media,
.bd {
    overflow: hidden;
    _overflow: visible;
    zoom: 1;
}
.media .img {
    float: left;
    margin-right: 10px;
}
.media .img img {
    display: block;
}

结果

这是一个容器的表现形式,有一个图片在链接中以及文本围绕在一侧:

挑战最佳CSS实战

新的需求出现时

假设我们现在需要将文本显示在图像的另一边。

模板结构

非常庆幸BFC的魔力,现在我们需要修改链接的样式风格。为此,我们给图片增加一个新的类名imgExt

<div class="media">
    <a href="http://twitter.com/thierrykoblentz" class="imgExt">
        <img src="thierry.jpg" alt="me" width="40" />
    </a>
    <div class="bd">
        @thierrykoblentz 14 minutes ago
    </div>
</div>

CSS

我们将添加一个向右浮动的样式给链接,并修改他们的margin值:

.media {
    margin: 10px;
}
.media,
.bd {
    overflow: hidden;
    _overflow: visible;
    zoom: 1;
}
.media .img {
    float: left;
    margin-right: 10px;
}
.media .img img {
    display: block;
}
.media .imgExt {
    float: right;
    margin-left: 10px;
}

结果

图像现在显示在另一边:

挑战最佳CSS实战

另一个需求出现时

假设我们现在需要在正常的页面中将文本设置的更小。为此,我们创建一个新规则,使用#rightRail作为上下文的选择器:

模板结构

我们的模块现在放在一个div#rightRail的容器里面:

<div id="rightRail">
    <div class="media">
        <a href="http://twitter.com/thierrykoblentz" class="img">
            <img src="thierry.jpg" alt="me" width="40" />
        </a>
        <div class="bd">
            @thierrykoblentz 14 minutes ago
        </div>
    </div>
</div>

CSS

再一次,我们创建一个额外的规则,这一次使用后代选择器#rightRail .bd:

.media {
    margin: 10px;
}
.media,
.bd {
    overflow: hidden;
    _overflow: visible;
    zoom: 1;
}
.media .img {
    float: left;
    margin-right: 10px;
}
.media .img img {
    display: block;
}
.media .imgExt {
    float: right;
    margin-left: 10px;
}
#rightRail .bd {
    font-size: smaller;
}

结果

这里是我们的原始模块,显示在div#rightRail内:

挑战最佳CSS实战

这个模块有什么错误?

  • 在样式表中给模块改变简单的样式可以得到新的效果:必须有一个方法不总是为需要的样式风格写更多的CSS规则。
  • 组合选择器是常见的样式(.media,.bd{}:使用了组合选择器,而没有使用一个类定义样同的样式,而导致更多的CSS。
  • 我们有六种规则,四种规则是基于上下文:上下文相关的规则是很难维护的。样式与这些规则不太可重用。
  • RTL和LTR接口变得复杂:改变方向,我们需要覆盖我们的一些样式(即写更多的规则)。例如:
.rtl .media .img {
    margin-right: auto; /* reset */
    float: right;
    margin-left: 10px;
}
.rtl .media .imgExt {
    margin-left: auto; /* reset */
    float: left;
    margin-right: 10px;
}

满足原子层叠样式表

a·tom·ic:组件中的单元组成一个更大的系统。

我们都知道,越小的单位,更可重用它。

把代码当作成乐高。把代码尽可可能断成小块的。——@csswizardry(via@stubbornella#btconf

将样式分解成单元块,我们可以将这些单元块映射到样式中的单个类,而不是很多。这个规则将导致一个更细粒度的规则,进而提高可重用性。

让我们重新考虑媒体对像使用这种新方法。

模板结构

我们使用了五个类名,没有哪个类是与内容相关的:

<div class="Bfc M-10">
    <a href="http://twitter.com/thierrykoblentz" class="Fl-start Mend-10">
        <img src="thierry.jpg" alt="me" width="40" />
    </a>
    <div class="Bfc Fz-s">
        @thierrykoblentz 14 minutes ago
    </div>
</div>

CSS

每个类都关联到一个指定的样式。在大多数情况下,这意味着我们有一个声明规则。

.Bfc {
    overflow: hidden;
    zoom: 1;
}
.M-10 {
    margin: 10px;
}
.Fl-start {
    float: left;
}
.Mend-10 {
    margin-right: 10px;
}
.Fz-s {
    font-size: smaller;
}

结果

挑战最佳CSS实战

这个如何?

现在我们忽略了类名和关注是这样做(或不这样做)。

  • 没有上下文样式:我们没用使用上下誩或后台选择器,这意味着我们的样式没有权重一说。
  • 方向(左和右)是“抽象的”:不用覆盖样式,容器在样式表中使用了RTL规则,例如下面的代码:
.Fl-start {
    float: right;
}
.Mend-10 {
    margin-left: 10px;
}

相同类名,相同的属性,不同的属性值。

但要注意最重要的事情是,我们美化模板样式。我们已经必变了模上下文样式。现在编辑HTML模板而不是样式表。

我相信这种方法是改变了,因为它大大缩小了范围。我们样式不是全局的,只是模块的和块级别的样式。我们可以改变一个模块的样式,而不用担心破坏别的页面。我们可以不做什么给这个样式表添加任何规则,只需要创建一个新类名和规则:

.someBasicStyleForThisElementHere {...}

这样一来没有冗余。选择器是不重复的,样式是属于一个单独的而不是其他的一部分。例如,在样式表中页面包含了72个float的声明。

同时,放弃一种样式——例如,总是保持图片在左侧的模块——这并不会任何样式规则过时。

听起来不错吧?

不是吹的吧?我听到了你说“这就违背了书中每一个规则”。这没有与行内样式比。你的类名是神秘的,但也是不具语义化的。

这是公平的。让我们解决这些问题。

关于没有语义化的类名

如果你查阅了W3C的网站管理的技巧,它说“好名字不改变”,你将看到这样的论点是关于维护,而不是语义化本身。所也这样说的也是,在CSS文件中修改样式要比在多个HTML文件中修改要来得容易多。如果只改变一个元素的样式而要求我们修改声明,.border4px将是一个不好的类名,这类名和名字联系在一起。换句话说:

.border4px {border-width:2px;}

关于语义模糊的类名

在大多数情况下,这些类名名称遵循Zen Coding的语法——查看Zen Coding字符表(PDF)——现在称为Emmet。换句话说,他们是简单的缩写。

关于(left和right)相关联的样式,包括一个组合声明。例如,Bfc代表“块格式化上下文”。

关于模仿行内样式

希望下图能帮助你清理:

挑战最佳CSS实战

  • 特殊性(Specificity):这项技术并不像@style具有特殊性。他的样式权重很低,因为都只是依赖于单个类名,而不像.parent .bd{}这样的后代选择器,选择为0.0.2.0(有关于权重的相关教程,请查阅CSS Specificity: Things You Should Know)。【中文版本可以点击《你应该知道的一些事情——CSS权重》】。
  • 冗余(Verbosity):大多数类是采用缩写来声明样式(例如,M-10表示的是margin:10px)。一些类名,如.Bfc是指多于一种样式的集合(如上图的映射部分)。还有其他一些类使用startend这样在关键词,而没使用leftright值(见上图中的抽象部分)。

下面是@style的一些优点:

  • 范围(Scope):样式就是将接点相连在一起的一个“沙箱”。
  • 可移植性(Portability):因为样式是封装在一些的,你可以移动到模块中而不会丢失他们的样式。当然,我们仍然需要样式表;但是,因为不受限于上下文结构,模块可以放置在任何一个页面上,网站中甚至是网络中。

臃肿的路径

因为模块的样式是直接依靠类名,他们可以实现我们想要的任何效果。例如,如果我们需要创建一个简单的两列布局,我们所需要做的是在模板中用div来替代当初的链接。看起来像这样:

<div class="Bfc M-10">
    <div class="Fl-start Mend-10 W-25">
        column 1
    </div>
    <div class="Bfc">
        column 2
    </div>
</div>

我们只需要在样式表中增加一个额外的规则:

.Bfc {
    overflow: hidden;
    zoom: 1;
}
.M-10 {
    margin: 10px;
}
.Fl-start {
    float: left;
}
.Mend-10 {
    margin-right: 10px;
}
.Fz-s {
    font-size: smaller;
}
.W-50 {
    width: 50%;
}

与传统方法相比:

<div class="wrapper">
    <div class="sidebar">
        column 1
    </div>
    <div class="content">
        sidebar
    </div>
</div>

这里我们需要创建三个新的类名,添加一个额外的样式规则和组选择器:

.wrapper,
.content,
.media,
.bd {
    overflow: hidden;
    _overflow: visible;
    zoom: 1;
}
.sidebar {
    width: 50%;
}
.sidebar,
.media .img {
    float: left;
    margin-right: 10px;
}
.media .img img {
    display: block;
}

我认为上面的代码很好的展示了SoC原则。根据我的经验,它只是增加了样式表大小。

此外,文件越大,越有由复杂的规则和选择器组成。然后没有人敢去编辑现有的样式规则:

  • 我们不敢去修改样式规则,害怕修改现有样式规则后会破坏一些东西。
  • 我们只能创建新的样式规则,而不是修改现有样式规则,因为我们不确定后者是100%安全的。

换句话来说,我们使事情变得更糟糕,因为我们使文件变得越来越臃肿。

如今,人们习惯于非常大的样式表,和很多程序员认为他们熟悉他们的领域。而不是去思考减少臃肿,他们使用工具(如预处理器)来帮助他们去处理它。Chris Eppstein告诉我们

LinkedIn有超过1100个Sass文件(每个SCSS文件有230K行)和每天有超过90个开发人员在编写Sass。

CSS臃肿对比HTML臃肿

让我们一起来面对它们:数据必须放对地方。考虑下面的两种结构

<div class="sidebar">   

<div class="Fl-start Mend-10 W-25"> 

在很多案例中,语义化的类名要比表像的类名使用更多的字节(.wrapper.Bfc对比)。但我不认为这是一个真正让人担忧的问题,相比之下,现在大多数应用都在使用data-属性。

这就是gzip进来的好处,因为高冗余的类名在整个文档将取得更好的压缩。同样的道理也适用于样式表中,我们有很多冗余的样式:

.M-1 {margin: 1px;}
.M-2 {margin: 2px;}
.M-4 {margin: 4px;}
.M-6 {margin: 6px;}
.M-8 {margin: 8px;}
etc.

缓存

表面上的规则不改变。样式表由这些成熟的工具集制作,作者可以找到他们所需要的一切。通过他们的特征,他们停止增大文件,成为不可变的。而不可变是缓存最友好的。

没有更好的.button类名?

这里讨论的技术并不是禁止语义化类名和规则,以及群组选择器声明样式。这个想法只是为了评估常用的方法的好处,而不是采用它作为事实上的技术来美化Web页面。换句话说,我们是限制“组件”方法的几个案例,这是很有意义的。

例如,你可能会发现我们的样式表中有以下规则。这些规则设定我们的样式,我们不创建简单的类或规则,确保跨浏览支持。

.button {
    display: inline-block;
    *display: inline;
    zoom: 1;
    font-size: bold 16px/2em Arial;
    height: 2em;
    box-shadow: inset 1px 1px 2px 0px #fff;
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0.05, #ededed), color-stop(1, #dfdfdf));
    background: linear-gradient(center top, #ededed 5%, #dfdfdf 100%);
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ededed', endColorstr='#dfdfdf');
    background-color: #ededed;
    color: #777;
    text-decoration: none;
    text-align: center;
    text-shadow: 1px 1px 2px #ffffff;
    border-radius: 4px;
    border: 2px solid #dcdcdc;
}
.modal {
    position: fixed;
    top: 50%;
    left: 50%;
    -webkit-transform: translate(-50%,-50%);
    -ms-transform: translate(-50%,-50%);
    transform: translate(-50%,-50%);
    *width: 600px;
    *margin-left: -300px;
    *top: 50px;
}
@media \0screen {
    .modal {
        width: 600px;
        margin-left: -300px;
        top: 50px;
    }
}

另一方面,你将在样式中看到如下所列的规则(即样式绑定到特定的模块),因为我喜欢将相同的风格使用多个类来实现:一个用于字体大小、颜色,一个用于浮动等。

.news-module {
    font-size: 14px;
    color: #555;
    float: left;
    width: 50%;
    padding: 10px;
    margin-right: 10px;
}
.testimonial {
    font-size: 16px;
    font-style: italic;
    color: #222;
    padding: 10px;
}

我们包括所有可能的风格在我们的样式表?

这个想法是为了拥有一个规则,作者可以选择他们想要的任何样式。跨网站的样式风格是常见的,足以让这些成为样式表中的一部分样式。如果一个风格太具体,那么我们就会依赖于@style(样式属性)。换句话说,我们宁愿修改标记而不是样式表。主要目的是创建一个规则表,解决各种设计模式。比如一个元素的float的基本规则可以添加一个helper类名来完成。

/**
 * one liner with ellipsis
 * 1. we inherit hyphens:auto from body, which would break "Ell" in table cells
 */
.Ell {
    max-width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    -webkit-hyphens: none; /* 1 */
    -ms-hyphens: none;
    -o-hyphens: none;
    hyphens: none;
}
/**
 * kinda line-clamp
 * two lines according to default font-size and line-height
 */
.LineClamp {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    font-size: 13px;
    line-height: 1.25;
    max-height: 32px;
    _height: 32px;
    overflow: hidden;
}
/**
 * reveals an hidden element on :hover or :focus
 * visibility can be forced by applying the class "RevealNested-on"
 * IE8+
 */
:root .NestedHidden {
    opacity: 0;
}
:root .NestedHidden:focus,
:root .RevealNested:hover .NestedHidden,
:root .RevealNested-on .NestedHidden {
    opacity: 1;
}

这种规模如何?

我们刚刚发布了一个全新的My Yahoo,在很大程度上依赖于这种技术。这是它与雅虎其他产品的对比(gzip压缩后):

挑战最佳CSS实战

我们样式表大约是17.9kb大小(约3kb是可调整的),它是可共享的(与样式表的其他属性)。原因是,没有任何规则包含了内容。

结束

因为表像的类名一直被视为“界外”,我们、社区没有真正调查他们的使用需要。事实上,在这个名字的最佳实践,我们已经驳回了每一个机会去发掘自己潜在的好处。

在雅虎,@renatoiwa,@StevenRCarlson正在开发的项目使用了这个新的CSS架构。【中文版本,可以点击这里阅读】。代码似乎是可预测、可重用、可维护和可扩展的。到目前为止,这些都是我们所经历的结果:

  • 少臃肿:我们可以不添加一行样式到样式表中创建整个模块。
  • 快速开发:样式由与内容无关的类控制,因此我们可以复制和粘贴现有模块。
  • 自由的RTL接口:使用startend关键词有很大意义。它为我们省去了为上下文RTL编写额外的样式规则。
  • 好的缓存:大量的CSS可以跨产品和属性共享。
  • 非常小的维护(CSS方面):只有一套小样式规则在随着时间变化。
  • 抽象少:没有必要在一个样式表中找出一个模板的样式规则。这是所有的标记。
  • 第三方开发:第三方可以把我们一个模板无需附加样式表(或样式块)应用到项目中。没有自定义规则,意味着第三方没有打破规则,没有恰当的名称空间。

注意,如果维护而言,维护CSS方面总比维护HTML方面容易,那么很简单,我们可以在CSS方面不做样式清理。但如果我们必须保持简洁,那么我们同样面对一些痛苦的事情。

最后注意

在几个星期前,我在一次聚会中听到Colt McAnlis说“工具,而不是规则”,快速搜索这个词返回这个

我们都需要接受新知识、新方法、新的最佳实践,我们需要能够分享他们。

译者手语:整个翻译依照原文线路进行,并在翻译过程略加了个人对技术的理解。如果翻译有不对之处,还烦请同行朋友指点。谢谢!

如需转载,烦请注明出处:

英文原文:http://coding.smashingmagazine.com/2013/10/21/challenging-css-best-practices-atomic-approach/

中文译文:htpp://www.w3cplus.com/css/challenging-css-best-practices-atomic-approach.html

Mercurial X Proximo
返回顶部