SassGuidelines中文版本之二:简介

上篇Sass Guidelines中文版本之一:相关信息主要介绍了Sass Guidelines相关的信息,那么在这一节中将进入正轨,向大家开始介绍Sass Guidelines有关于Sass的相关内容。主要介绍了为什么要一份这样的指南,其原则是什么,以及Sass的一些语法书写规范围等等

简介

为什么需要一个样式指南?

一个样式指南并不是一份便于阅读并使代码处于理想状态的文档。它在一个项目的生命周期中是一份关键文档,描述了编写代码的方式和采用这样方式的原因。它可能在小项目中显得有些矫枉过正,但却能在保持代码库整洁,提高可扩展性和可维护性上提供诸多便利。

不用多说相信你也可以理解,参与项目的开发者越多,代码样式指南就越显的必要。与之相同,项目的规模越大,代码样式指南也会越加重要。

Harry RobertsCSS Guidelines就非常好:

样式指南(注意不是视觉风格指南)用于团队是一个很有价值工具:

免责声明

首先第一件事是:这不是一份CSS样式指南。本文档不会讨论诸如约定CSS类名、模块化开发模式和有关ID的疑惑等CSS范畴内的问题。本文档中的准则只着眼于处理Sass的专有内容。

此外,这份样式指南是我独创的,所以会显得有些个人主观倾向。你可以将它看成是我通过多年实践研究出的方法和建议的集合。这也让我有机会接触到少数极具见地的资源,所以一定要浏览一下扩展阅读

显然,这里讲的肯定不是进行Sass编程的唯一方式,而且它是否符合你的项目要求还有待检验。你可以随意选择,并使其适应需求。正如我们所说的,具体情况具体分析

核心原则

最后,如果有一件事是我想从整个样式指南中传授的,那就是:Sass以简为美,简约至上

感谢我过去使用Sass时傻傻的尝试,比如 bitwise operatorsiterators and generatorsa JSON parser,从而认识到了可以用预处理器来做什么。

同时,CSS是一门简单的语言,那么Sass在书写常规CSS的时候就不应该更复杂。KISS principle (Keep It Simple Stupid)在这里是一个核心原则,甚至在有些情况下要优先于DRY principle (Don't Repeat Yourself)。

有时候,一点点重复可以更好的保持代码的可维护性,而不是建立一个头重脚轻、臃肿复杂、不可维护的系统。

此外,请允许我再一次引用Harry Roberts的观点,实用胜过完美。有些时候,你可能会发现自己违背了这里所描述的规则。如果感觉自己的方式有道理,感觉很正确,那就继续做吧。编写代码从来都不是一家之言。

扩展阅读

语法格式

如果你问我一个样式指南首先要描述什么,我会告诉你:编写代码的通用准则。

当几个开发者在同一项目中编写CSS时,迟早会陷入各自为政的境地。编码样式指南通过规范统一的样式,不仅防止了这种混乱现象,也减轻了阅读和维护代码的难度。

概括地说,我们希望做到(受CSS Guidelines所启发):

    // 推荐方式
    .foo {
      display: block;
      overflow: hidden;
      padding: 0 1em;
    }
    // 不推荐方式
    .foo {
        display: block; overflow: hidden;
        padding: 0 1em;
    }

在本部分中不会涉及有关文件组织的问题,相关讨论在另一节中。

字符串

CSS中不要求字符串必须用引号包裹,甚至是字符串中包含空格的。就拿font-family属性来说:无论你是否使用引号包裹,CSS解析器都可以正确解析。

因此,Sass也不强制要求字符串必须被引号包裹。更棒的是(你也会如此认为),被引号包裹和没被包裹的一对字符串完全等同(例如,'abc' 严格等同于 abc)。

话虽如此,不强制要求字符串被引号包裹的毕竟是少数,所以,在Sass中字符串应该始终被单引号所包裹(在querty键盘中单引号比双引号更容易输入)。即使不考虑与其他语言的一致性,单是考虑CSS的近亲JavaScript,也有数条理由这么做:

    //推荐方式
    $font-stack: 'Helvetica Neue Light', 'Helvetica', 'Arial', sans-serif;
    //不推荐方式
    $font-stack: "Helvetica Neue Light", "Helvetica", "Arial", sans-serif;
    //不推荐方式
    $font-stack: Helvetica Neue Light, Helvetica, Arial, sans-serif;

注:在前面的示例中,sans-serif并没有用引号''包裹起来,因为它是CSS指定的一样属性值,所以不需要使用引号包裹起来。

URL最好也用引号包裹起来,原因和上面所描述一样:

    // 推荐方式
    .foo {
      background-image: url('/images/kittens.jpg');
    }
    // 不推荐方式
    .foo {
      background-image: url(/images/kittens.jpg);
    }

扩展阅读

数字

在Sass中,数字类型包括了长度、持续时间、频率、角度等等无单位数字类型。Sass允许在运行中计算这些度量值。

零值

当数字小于1时,应该在小数点前写出0.永远不要显示小数尾部的0

// 推荐方式
.foo {
  padding: 2em;
  opacity: 0.5;
}
// 不推荐方式
.foo {
  padding: 2.0em;
  opacity: .5;
}

单位

当定义长度时,0 后面不需要加单位。

// 推荐方式
$length: 0;
// 不推荐方式
$length: 0em;

在Sass中最常见的错误,是简单地认为单位只是字符串,认为它会被安全的添加到数字后面。这虽然听起来不错,但却不是单位正确的解析方式。可以把单位认为是代数符号,例如,在现实世界中,5英寸乘以5英寸得到25英寸。Sass也适用这样的逻辑。

将一个单位添加给数字的时候,实际上是让该数值乘以1个单位

$value: 42;
// 推荐方式
$length: $value * 1px;
// 不推荐方式
$length: $value + px;

需要注意的是加上一个0unit也可以正确解析,但是这种方式在某种程度上会造成一些混乱,所以我更愿意推荐上面的方式。事实上,将一个数字转换为其他兼容单位时,加0操作并不会造成错误。

$value: 42 + 0px;
// -> 42px
$value: 1in + 0px;
// -> 1in
$value: 0px + 1in;
// -> 96px

这一切最终取决于你想要达到怎样的效果。只要记住,像添加一个字符串一样添加一个单位并不是一种好的处理方式。

要删除一个值的单位,你需要除以相同类型的1单位

$length: 42px;
// 推荐方式
$value: $length / 1px;
// 不推荐方式
$value: str-slice($length + unquote(''), 1, 2);

给一个数值以字符串形式添加单位的结果是产生一个字符串,同时要防止对数据的额外操作。从一个带有单位的数值中分离数字部分也会产生字符串,但这些都不是你想要的。

计算

最高级运算应该始终被包裹在括号中。这么做不仅是为了提高可读性,也是为了防止一些Sass强制要求对括号内内容计算的极端情况。

// 推荐方式
.foo {
  width: (100% / 3);
}
// 不推荐方式
.foo {
  width: 100% / 3;
}

幻数

"幻数",是古老的学校编程未命名数值常数的命名。基本上,它们只是能工作™但没有任何逻辑思维的随机数。

相信不用多说你也会理解,幻数是一场瘟疫,应不惜一切代价以避免。当你对数值的解析方式无法找到一个合理解释时,你可以对此提交一个内容宽泛的评论,包括你是怎样遇见这个问题以及你认为它应该怎样工作。承认自己不清楚一些机制的解析方式,远比让以后的开发者从零开始弄清它们更有帮助。

/**
 * 1. Magic number. This value is the lowest I could find to align the top of
 * `.foo` with its parent. Ideally, we should fix it properly.
 */
.foo {
  top: 0.327em; /* 1 */
}

扩展阅读

颜色

颜色在CSS中占有重要地位。当涉及到操纵色彩时,Sass通过提供少数的函数功能,最终成为了极具价值的助手。

颜色格式

为了尽可能简单地使用颜色,我建议颜色格式要按照以下顺序排列:

对于初学者来说,颜色关键字往往比较通俗易懂。HSL表示方式不仅仅是人类大脑最易于接受的,它也可以让样式表作者轻松地调整色调、饱和度和亮度来修改颜色。如果一个颜色偏蓝、偏绿或者偏红,那么使用RGB更容易表示出来,但是却不容易表示三者的混合色。最后,十六进制是人类大脑理解的极限了。

// 推荐方式
.foo {
  color: red;
}
// 不推荐方式
.foo {
  color: #FF0000;
}

使用HSL值或者RGB值,通常在逗号 (,)后面追加一个空格,而不在前后括号 ((, )) 和值之间添加空格。

// 推荐方式
.foo {
  color: rgba(0, 0, 0, 0.1);
  background: hsl(300, 100%, 100%);
}
// 不推荐方式
.foo {
  color: rgba(0,0,0,0.1);
  background: hsl( 300, 100%, 100% );
}

颜色和变量

当一个颜色被多次调用时,最好用一个有意义的变量名来保存它。

$sass-pink: #c69;

现在,你就可以在任何需要的地方随意使用这个变量了。不过,如果你是在一个主题中使用,我不建议固定的使用这个变量。相反,可以使用另一个标识方式的变量来保存它。

$main-theme-color: $sass-pink

这样做可以防止一个主题变化而出现此类结果 $sass-pink: blue

变量和变暗颜色

lightendarken 函数都是通过增加或者减小HSL中颜色的亮度来实现调节的。基本上,它们就是adjust-color函数添加了 $lightness参数的别名。

问题是,这些函数经常并不能实现预期的结果。另一方面,通过混合白色黑色实现变量或变暗的 mix函数,是一个不错的方法。

和上述两个函数相比,使用mix的好处是,当你降低颜色的比例时,它会渐进的接近黑色(或者白色),而 darkenlighten立即变换颜色到黑色或白色。

变量和变暗颜色

KatieK通过Illustration绘制了lighten/darkenmix之间的差别

如果你不想每次都写mix函数,你可以创建两个易用的 tintshade (Compass的一部分)来处理相同的事:

// Slightly lighten a color
// @access public
// @param {Color} $color - color to tint
// @param {Number} $percentage - percentage of `$color` in returned color
// @return {Color}
@function tint($color, $percentage) {
  @return mix($color, white, $percentage);
}
// Slightly darken a color
// @access public
// @param {Color} $color - color to shade
// @param {Number} $percentage - percentage of `$color` in returned color
// @return {Color}
@function shade($color, $percentage) {
  @return mix($color, black, $percentage);
}

注:scale-color函数的设计初衷是为了更流畅地调试属性——以实际的高低为调试基础。它如同mix一样好用,并且提供了更清晰地调用约定。比例因子并不完全相同。

扩展阅读

列表

列表就是Sass的数组。列表是一个一维的数据结构(不同于maps),用于保存任意类型的数值(包括列表,从而产生嵌套列表)。

列表需要遵守以下规范:

    // 推荐方式
    $font-stack: 'Helvetica', 'Arial', sans-serif;
    // 不推荐方式
    $font-stack:
      'Helvetica',
      'Arial',
      sans-serif;
    
    // 不推荐方式
    $font-stack: 'Helvetica' 'Arial' sans-serif;
    
    // 不推荐方式
    $font-stack: ('Helvetica', 'Arial', sans-serif);
    
    // 不推荐方式
    $font-stack: ('Helvetica', 'Arial', sans-serif,);

当需要给列表添加一个新列表项时,请遵守其提供的API,不要试图手动给列表添加列表项。

$shadows: 0 42px 13.37px hotpink;
// 推荐方式
$shadows: append($shadows, $shadow, comma);
// 不推荐方式
$shadows: $shadows, $shadow;

扩展阅读

Maps

从Sass3.3开始,样式表作者可以使用map这种数据结构——Sass团队使map可以映射关联数组、哈希表甚至是Javascript对象。map是一种映射任何类型键值对(可以是任何类型,包括内嵌maps,不过不推荐这种内嵌方式)的数据结构。

map的使用应该遵循下述规范:

示例:

// 推荐方式
$breakpoints: (
  'small': 767px,
  'medium': 992px,
  'large': 1200px,
);
// 不推荐方式
$breakpoints: ( small: 767px, medium: 992px, large: 1200px );

调试Sass的map

如果你感到困惑并想了解Sass的map到底有怎样的魔力,请不要担心,Sass中始终存在一个自动保存运行过程的机制。

@mixin debug-map($map) {
  @at-root {
    @debug-map {
      __toString__: inspect($map);
      __length__: length($map);
      __depth__: if(function-exists('map-depth'), map-depth($map), null);
      __keys__: map-keys($map);
      __properties__ {
        @each $key, $value in $map {
          #{'(' + type-of($value) + ') ' + $key}: inspect($value);
        }
      }
    }
  }
}
=debug-map($map)
  @at-root
    @debug-map
      __toString__: inspect($map)
      __length__: length($map)
      __depth__: if(function-exists('map-depth'), map-depth($map), null)
      __keys__: map-keys($map)
      __properties__
        @each $key, $value in $map
          #{'(' + type-of($value) + ') ' + $key}: inspect($value)

如果你想深入了解map的实现机制,可以添加下述函数。该混合宏可以自动显示map的运行机制。

/// Compute the maximum depth of a map
/// @param {Map} $map
/// @return {Number} max depth of `$map`
@function map-depth($map) {
  $level: 1;
  @each $key, $value in $map {
    @if type-of($value) == 'map' {
      $level: max(map-depth($value) + 1, $level);
    }
  }
  @return $level;
}

扩展阅读

CSS 规则集

在这里,极有可能颠覆每个人对书写CSS规则集的认知(根据众多规范整理而成,包括CSS Guidelines):

示例:

// 推荐方式
.foo, .foo-bar,
.baz {
  display: block;
  overflow: hidden;
  margin: 0 auto;
}
// 不推荐方式
.foo,
.foo-bar, .baz {
    display: block;
    overflow: hidden;
    margin: 0 auto }

添加与CSS相关的规范时,我们需要注意:

示例:

.foo, .foo-bar,
.baz {
  $length: 42em;
  @include ellipsis;
  @include size($length);
  display: block;
  overflow: hidden;
  margin: 0 auto;
  &:hover {
    color: red;
  }
  @include respond-to('small') {
    overflow: visible;
  }
}

扩展阅读

声明顺序

难以想象竟有这么多关于划分CSS声明顺序的讨论。具体而言,有如下两派:

这两种方式各有利弊。一方面,字母排序方式通俗易懂(至少对于语言中使用拉丁字母的人来说),所以排序的过程完全没有争议。但是,这种排序的结果却十分奇怪,如bottomtop竟然彼此不相邻。为什么animations属性出现在display属性之前?字母排序方式有太多诸如此类的怪相了。

.foo {
  background: black;
  bottom: 0;
  color: white;
  font-weight: bold;
  font-size: 1.5em;
  height: 100px;
  overflow: hidden;
  position: absolute;
  right: 0;
  width: 100px;
}
.foo
  background: black
  bottom: 0
  color: white
  font-weight: bold
  font-size: 1.5em
  height: 100px
  overflow: hidden
  position: absolute
  right: 0
  width: 100px

另一方面,按照类型排序则让属性显得更具有意义。每个和字体相关的属性被声明在一起,topbottom也结合在一起,最终审阅CSS规则集感觉就像是在读故事。除非你坚持诸如 Idiomatic CSS的规定,不然类型声明顺序可以有更丰富充实的表现。white-space应该放在哪里:font还是dispaly? overflow应该归属何处?如何进行组内排序(如果是字母排序,这岂不成了个笑话)?

.foo {
  height: 100px;
  width: 100px;
  overflow: hidden;
  position: absolute;
  bottom: 0;
  right: 0;
  background: black;
  color: white;
  font-weight: bold;
  font-size: 1.5em;
}

此外也有其他类型排序的分支,比如Concentric CSS,他看起来相当流行。Concentric CSS的基础是依赖盒模型定义顺序:由外而内。

.foo {
  width: 100px;
  height: 100px;
  position: absolute;
  right: 0;
  bottom: 0;
  background: black;
  overflow: hidden;
  color: white;
  font-weight: bold;
  font-size: 1.5em;
}

我必须说我不能对此下任何判定。一份CSS-Tricks做的统计报告确认,超过45%的开发者使用类型顺序声明,而只有14%使用字母顺序。此外还有39%的开发者随意而为,这其中就包括我。

CSS-Tricks做的统计报告

因此,我不会在此强加规范选择怎样的声明顺序。只要你长久的在自己的样式表中保持一致的风格,那么选择喜欢的声明顺序就可以了。

注:最近一份研究报告表明,使用CSS Comb(一种排序的类型)对CSS进行排序,按类型顺序声明,Gzip压缩文件大小平均达到2.7%,而按字母顺序排序压缩的文件大小平均达到1.3%。

扩展阅读

选择器嵌套

Sass中一个正在被众多开发者滥用的功能,就是选择器嵌套。选择器嵌套为样式表作者提供了一个通过局部选择器的相互嵌套实现全局选择的方法。

一般规则

比如下述Sass选择器的嵌套:

.foo {
  .bar {
    &:hover {
      color: red;
    }
  }
}

生成的CSS:

.foo .bar:hover {
  color: red;
}

从Sass3.3开始,可以在同一行中使用最近选择器引用(&)来实现高级选择器,比如:

.foo {
  &-bar {
    color: red;
  }
}

生成CSS:

.foo-bar {
  color: red;
}

这种方式通常被用来配合BEM全名方式使用,基于原选择器(比如.block)生成.block__element and .block--modifier选择器。

注:传说中,使用&能在当前选择器下产生新的选择器,这样代码库中选择器无法控制,因为他们本身不存在。

选择器嵌套最大的问题是将使最终的代码难以阅读。开发者需要花费巨大精力计算不同缩进级别下选择器具体的表现效果。CSS最终的表现效果往往不是浅显易懂的。

选择器越具体则声明语句越冗长,而且对最近选择器的引用(&)也越频繁。在某些时候,出现混淆选择器路径和探索下一级选择器的错误率很高,这非常不值得。

特殊规则

为了防止这种情况,我们应该避免在伪类和伪元素外使用选择器嵌套。这是唯一允许和建议使用的选择器嵌套方式。

.foo {
  color: red;
  &:hover {
    color: green;
  }
  &::before {
    content: 'pseudo-element';
  }
}

使用选择器嵌套选择伪类和伪元素不仅仅有道理的(因为它的处理功能与选择器紧密相关),而且有助于保持总体的一致性。

当然,如果使用类似.is-active类名来控制当前选择器状态,也可以这样使用选择器嵌套。

.foo {
  // ...
  &.is-active {
    font-weight: bold;
  }
}

这并不是最重要的,当一个元素的样式在另一个容器中有其他指定的样式时,可以使用嵌套选择器让他们保持在同一个地方。

.foo {
  // ...
  .no-opacity & {
    display: none;
  }
}

当一个没太多经验的开发者,不对类似于.no-opacity &这样的选择器造成混淆。可能创建一个mixin来处理。

/// Helper mixin to provide simple API to selector nesting
/// @param {String} $selector - Selector
@mixin when-inside($selector) {
  #{$selector} & {
    @content;
  }
}

重写刚才的示例,他看起来像这样:

.foo {
  // ...
  @include when-inside('.no-opacity') {
    display: none;
  }
}

这所有的一切,有些是无关紧要的细节,关键是要保持一致性。如果你觉得完全有信心搞定选择器嵌套,然后你就使用了选择器嵌套。可你还要确保你的整个团队也能搞定选择器的嵌套。

扩展阅读

本文根据Hugo Giraudel的《Sass Guidelines》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:Sass Guidelines

出处:http://www.w3cplus.com/preprocessor/sass-guidelin-part-2.html