这是我参与新手入门的第 2 篇文章
虽然看起来是一份样式指南,其实包含了大部分 Sass/SCSS 的使用
对于样式规范使用自动格式化的工具是非常有用的
本没有说过这句话,但是说的人多的也就说过了这句话。
CSS with superpowers
Sass/SCSS is the most mature, stable, and powerful professional grade CSS extension language in the world.
使用指引
变量
在 Sass 中,允许定义变量并且允许在运行中计算这些值。
创建新变量的准则:
- 该值至少重复出现了两次
- 该值至少可能会被更新一次
- 该值 非巧合情况下 所有的表现都与变量有关
作用域
通俗来讲就是局部声明的变量会覆盖全局变量,称之为 全局变量影子。
// Initialize a global variable at root level.
// 在根级别初始化一个全局变量
$variable: 'initial value';
// Create a mixin that overrides that global variable.
// 创建一个重写全局变量的 @mixin
@mixin global-variable-overriding {
$variable: 'mixin value' !global;
}
.local-scope::before {
// Create a local variable that shadows the global one.
// 创建一个全局变量影子变量
$variable: 'local value';
// Include the mixin: it overrides the global variable.
// 使用 @include @xxx(mixin):重写全局变量
@include global-variable-overriding;
// Print the variable’s value.
// It is the **local** one, since it shadows the global one.
// 打印变量的值
// 这是本地的那个,因为它是全局影子变量
content: $variable;
}
// Print the variable in another selector that does no shadowing.
// It is the **global** one, as expected.
// 在另一个没有做(没有声明全局影子变量)选择器内打印变量
// 如预期的,这是全局的那个变量
.other-local-scope::before {
content: $variable;
}
复制代码
!default
标识符
使用 !default
标志来定义变量,方便其他开发者重写变量。
$baseline: 1em !default;
复制代码
开发者才能在引入你的库之前定义自用的 $baseline,引入后又不必担心自己的值被重定义了。
// Developer’s own variable
// 开发者自己的变量
$baseline: 2em;
// Your library declaring `$baseline`
// 库里声明了 `$baseline`
@import 'your-library';
// $baseline == 2em;
复制代码
!global
标识符
!global
标志应该只在局部范围的全局变量被覆盖时使用。定义根级别的变量时,!global
标志应该省略。
// ?
$baseline: 2em;
// ?
$baseline: 2em !global;
复制代码
多变量与 Maps
优势:
- 能够遍历
- 使用
map-get()
函数,提供友好 API 功能
/// Z-indexes map, gathering all Z layers of the application
/// Z-indexed map, 收集应用中所有 Z 层
/// @access private 私有的
/// @type Map Map类型
/// @prop {String} key - Layer’s name 层的名字?变量的标识
/// @prop {Number} value - Z value mapped to the key 标识对应的值
$z-indexes: (
'modal': 5000,
'dropdown': 4000,
'default': 1,
'below': -1,
);
/// Get a z-index value from a layer name
/// 用标识获取对应的变量值
/// @access public 公共的权限,大家都能获取
/// @param {String} $layer - Layer’s name
/// @return {Number}
/// @require $z-indexes
@function z($layer) {
@return map-get($z-indexes, $layer);
}
复制代码
扩展
// input scss
/* This CSS will print because %message-shared is extended. */
%message-shared {
border: 1px solid #ccc;
padding: 10px;
color: #333;
}
// This CSS won't print because %equal-heights is never extended.
// 并没有被使用(继承)到
%equal-heights {
display: flex;
flex-wrap: wrap;
}
.message {
@extend %message-shared;
}
.success {
@extend %message-shared;
border-color: green;
}
.error {
@extend %message-shared;
border-color: red;
}
.warning {
@extend %message-shared;
border-color: yellow;
}
复制代码
// output css
/* This CSS will print because %message-shared is extended. */
.message, .success, .error, .warning {
border: 1px solid #ccc;
padding: 10px;
color: #333;
}
.success {
border-color: green;
}
.error {
border-color: red;
}
.warning {
border-color: yellow;
}
复制代码
有关更多,本人菜才疏学浅,不能理解其意,无法做出归纳总结。
混合宏
@mixin
重用和减少重复组件的关键,使用 @mixin
的关键是简洁,当然也不要过度逻辑化你的代码,尽量保持一切简洁。如果一个 @mixin
最后超过了 20 行,那么它应该被分离成更小的块甚至是重写。
如果你发现有一组 CSS 属性经常不是因为巧合一起出现,那么你就可以使用 @mixin
来代替:
/// 清除浮动
@mixin clearfix {
&::after {
content: '';
display: table;
clear: both;
}
}
/// 设定宽高
/// @param {Length} $width
/// @param {Length} $height
@mixin size($width, $height: $width) {
width: $width;
height: $height;
}
复制代码
@include
有时候使用宏单纯为了避免重复申明相同样式,这时我们使用 @include
混入宏可以省略参数,同时应该删除括号以保持简洁。
// ?
.foo {
@include center;
}
// ?
.foo {
@include center();
}
复制代码
参数列表
大概就等于 js 扩展运算符 ...
?
@mixin dummy($a, $b, $c) {
// …
}
// ?
@include dummy(true, 42, 'kittens');
// ? but ?
$params: (true, 42, 'kittens');
$value: dummy(nth($params, 1), nth($params, 2), nth($params, 3));
// ?
$params: (true, 42, 'kittens');
@include dummy($params...);
// ?
$params: (
'c': 'kittens',
'a': true,
'b': 42,
);
@include dummy($params...);
复制代码
混合宏和浏览器前缀
我推荐使用 Autoprefixer Bourbon Compass
只有当你遇到以上情况解决不了的情况再考虑如下方法,切记不要无脑使用前缀:
// ?
@mixin transform($value) {
-webkit-transform: $value;
-moz-transform: $value;
transform: $value;
}
/// Mixin helper to output vendor prefixes
/// 输出浏览器供应商的前缀的 Mixin 助手
/// @access public 公共的获取权限
/// @author KittyGiraudel 作者 KittyGiraudel
/// @param {String} $property - Unprefixed CSS property 没有 CSS 前缀的属性
/// @param {*} $value - Raw CSS value 原始 CSS 值
/// @param {List} $prefixes - List of prefixes to output 输出的前缀列表
@mixin prefix($property, $value, $prefixes: ()) {
@each $prefix in $prefixes {
-#{$prefix}-#{$property}: $value;
}
#{$property}: $value;
}
// how to use 如何使用
.foo {
@include prefix(transform, rotate(90deg), ('webkit', 'ms'));
}
复制代码
条件语句
Sass 通过 @if
和 @else
指令提供了条件语句。除非你的代码确实如此复杂,否则没必要在日常开发中使用条件语句,其主要适用于框架或库。如果使用了条件语句,请遵守以下规则:
- 除非必要,不然不需要括号
// ?
@if $support-legacy {
// …
} @else {
// …
}
// ?
@if ($support-legacy == true) {
// …
}
@else {
// …
}
复制代码
测试一个错误值时,通常使用 not
关键字 而不是比较与 false 或 null 等值。
// ?
@if not index($list, $item) {
// …
}
// ?
@if index($list, $item) == null {
// …
}
复制代码
当使用条件语句并在一些条件下有内联函数返回不同结果时,始终要确保最外层函数有一个 @return
语句,这和函数设置了返回值就必须所有分支必须要有返回值不一样。
// ?
@function dummy($condition) {
@if $condition {
@return true;
}
@return false;
}
// ?
@function dummy($condition) {
@if $condition {
@return true;
} @else {
@return false;
}
}
复制代码
循环
@each
@each
是最实用的,有两种使用方式,但是第二种更加实用。
@each $theme in $themes {
.section-#{$theme} {
background-color: map-get($colors, $theme);
}
}
@each $key, $value in $map {
.section-#{$key} {
background-color: $value;
}
}
复制代码
@for
当需要聚合伪类 :nth-*
的时候,使用 @for
循环很有用。除了这些使用场景,如果必须迭代最好还是使用 @each
循环。
@for $i from 1 through 10 {
.foo:nth-of-type(#{$i}) {
border-color: hsl($i * 36, 50%, 50%);
}
}
复制代码
不要使用
@while
警告和错误 & 工具
当你需要用到的时候再去寻找吧,那样更加得高效
风格指南
- 使用 2 个制表符大小的 tab 键
- 理想上,每行保持为80个字符宽度
- 正确书写多行CSS规则
- 有意义的使用空格
// ?
.foo {
display: block;
overflow: hidden;
padding: 0 1em;
}
// ?
.foo {
display: block; overflow: hidden;
padding: 0 1em;
}
复制代码
字符串
编码
为了避免潜在的字符编码问题,强力建议在入口文件中通过 @charset 指令使用 UTF-8 编码格式。
请确保该指令是文件的第一条语句,并排除其他字符编码声明。
@charset 'utf-8';
复制代码
引用
Sass 中字符串应该始终被单引号 '
所包裹,理由如下:
- 如果颜色名不被引号包裹,将会被解析为颜色值,显然这会导致严重问题
- 大多数的语法高亮机制将会因未被引号包裹的字符串而罢工
- 提高可读性
- 没有正当理由不去用引号包裹字符串
// ?
$direction: 'left';
// ?
$direction: left;
复制代码
@charset
根据 CSS 应该用双引号包裹才是有效的,但是 SCSS 修正了相关信息,无论如何都能生成正确的代码
作为 CSS 的值
CSS 中类似 initial
或 sans-serif
的标识符无须引用起来。事实上,font-family: 'sans-serif'
该声明是错误的,因为 CSS 希望获得的是一个标识符,而不是一个字符串。因此,我们无须引用这些值。
// ?
$font-type: sans-serif;
// ?
$font-type: 'sans-serif';
// Okay I guess 也许还不错
$font-type: unquote('sans-serif');
复制代码
就像上例这样,我们就可以区别用于 CSS 值的字符串(CSS 标识符)和 Sass 的字符串类型了(比如 map 的值)。
我们没有引用前者,但却使用单引号包裹了它。
包含引号的字符串
如果字符串内包含了一个或多个单引号,一种解决方案就是使用双引号包裹整个字符串,从而避免使用转义字符。
URL 最好也用引号包裹起来,原因和上面所描述一样:
// Okay 还行
@warn 'You can\'t do that.';
// Okay 还行
@warn "You can't do that.";
// ?
.foo {
background-image: url('/images/kittens.jpg');
}
// ?
.foo {
background-image: url(/images/kittens.jpg);
}
复制代码
数字
零值
当数字小于 1 时,应该在小数点前写出 0. 永远不要显示小数尾部的 0。
// ?
.foo {
padding: 2em;
opacity: 0.5;
}
// ?
.foo {
padding: 2.0em;
opacity: .5;
}
复制代码
单位
当定义 长度 时,0 后面不需要加单位。
// ?
$length: 0;
// ?
$length: 0em;
复制代码
计算
高级运算应该始终被包裹在括号中。这么做不仅是为了提高可读性,也是为了防止一些 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 */
}
复制代码
颜色
推荐使用顺序:
- HSL 值;
- RGB 值;
- 十六进制(使用小写并尽可能简写)
不建议使用 CSS 颜色关键字。
颜色关键字的语义化并不准确,比如 grey
比 darkgrey
的颜色更深一些;grey
和 gray
之间的差别也会造成一致性的问题。别忘记在 ,
后添加空格:
// ?
.foo {
color: hsl(0, 100%, 50%);
}
// Also ?
.foo {
color: rgb(255, 0, 0);
}
// Meh
.foo {
color: #f00;
}
// ?
.foo {
color: #FF0000;
}
// ?
.foo {
color: red;
}
复制代码
不过真的有人记得住这么多的颜色值吗?还是说大家都是网上找的颜色或者设计师给的?
颜色和变量
当一个颜色被多次调用时,最好其实必须用一个有意义的变量名来保存它。
$sass-pink: hsl(330, 50%, 60%);
复制代码
不过,如果你是在一个主题中使用,不建议固定的使用这个变量。你应该再使用另一个标识方式的变量来保存它。
$main-theme-color: $sass-pink;
复制代码
防止主题变化而出现此类结果 $sass-pink: blue
。
列表
遵守以下规范:
- 除非适用于 CSS,否则应该始终使用逗号作为分隔符
- 列表太长不能写在 80 字符宽度的单行中时使用多行形式,否则应该始终单行显示
- 始终使用括号包裹
并且使用提供的 API 进行添加
// ?
$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,);
$shadows: (0 42px 13.37px hotpink);
// ?
$shadows: append($shadows, $shadow, comma);
// ?
$shadows: $shadows, $shadow;
复制代码
Map
- 冒号
:
之后添加空格 - 如果键是字符串(99% 都是字符串),则使用括号包裹起来
- 每一个键值对单独一行
- 为最后一个键值对添加尾部逗号
,
,方便添加新键值对、删除和重排已有键值对
// ?
$breakpoints: (
'small': 767px,
'medium': 992px,
'large': 1200px,
);
// ?
$breakpoints: ( small: 767px, medium: 992px, large: 1200px );
复制代码
CSS 规则集
- 相关联的选择器写在同一行,不相关联选择器分行书写
- 最后一个选择器和左开大括号
{
中间书写一个空格 - 每个声明单独一行
- 冒号
:
后添加空格 - 所有声明的尾部都添加一个分号
;
// ?
.foo, .foo-bar,
.baz {
display: block;
overflow: hidden;
margin: 0 auto;
}
// ?
.foo,
.foo-bar, .baz {
display: block;
overflow: hidden;
margin: 0 auto }
复制代码
声明顺序
随你
确实如此,并没有规定如何排序,大家使用的规则也各不相同,但是我相信,如果正确的对 CSS 进行拆分并且添加注释,阅读起来不是什么难事。
选择器嵌套
如其名
// input scss
.foo {
.bar {
&:hover {
color: red;
}
}
}
// output css
.foo .bar:hover {
color: red;
}
复制代码
自动 SCSS 3.3 开始,可以在同一行中使用最近选择器引用 &
来实现高级选择器:
// input scss
.foo {
&-bar {
color: red;
}
}
// output css
.foo-bar {
color: red;
}
复制代码
- 建议嵌套不要超过三层
- 或者避免使用嵌套
其实现在的选择器也是挺好用的
当然也是有例外的
允许且推荐 在最外层选择器中嵌套伪类和伪元素,类似 .is-active
类名来控制当前选择器状态,也可以这样使用选择器嵌套:
.foo {
color: red;
&:hover {
color: green;
}
&::before {
content: 'pseudo-element';
}
}
.foo {
// …
&.is-active {
font-weight: bold;
}
}
复制代码
命名约定
除了常量使用全大写字符外,其余使用 CSS-y 的风格:小写连字符分隔,有意义的命名,命名空间同样食用。
$vertical-rhythm-baseline: 1.5rem;
@mixin size($width, $height: $width) {
// …
}
@function opposite-direction($direction) {
// …
}
// ?
$CSS_POSITIONS: (top, right, bottom, left, center);
// ?
$css-positions: (top, right, bottom, left, center);
$su-configuration: ( … );
@function su-rainbow($unicorn) {
// …
}
复制代码
注释
- CSS 规则集之前使用 c 风格
/** ... */
的注释 - 特定部分使用单行
//
注释 - 搭配 SassDoc 请使用
///
结构
请根据 项目/组件 进行拆分
响应式设计和断点
应该不针对特定设备使用媒体查询。尤其不应该专门针对某设备型号设计媒体查询。媒体查询应该关注屏幕尺寸,直到当前设计遇到阻断再继续下一个媒体查询。
毕竟现在手机?像砖头,还有 「你的下一台电脑何必是电脑」。
听说响应式设计应该从手机做起是这样吗?没有太多响应式设计的经验,可以在评论区给出你的观点!
// ?
$breakpoints: (
'medium': (min-width: 800px),
'large': (min-width: 1000px),
'huge': (min-width: 1200px),
);
// ?
$breakpoints: (
'tablet': (min-width: 800px),
'computer': (min-width: 1000px),
'tv': (min-width: 1200px),
);
复制代码
Tips
不要简单地认为单位只是字符串,认为它会被安全的添加到数字后面。可以把单位认为是代数符号,例如,在现实世界中,5 英寸乘以 5 英寸得到 25 英寸。Sass 也适用这样的逻辑。
将一个单位添加给数字的时候,应该让该数值乘以 1${需要添加的单位}
。
不要使用传统编程语言的字符串拼接来添加单位
$value: 42;
// ?
$length: $value * 1px;
// ?
$length: $value + px;
复制代码
$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);
复制代码
做点什么
夏天真是太热了,最好能下点雪!
<div class="snow"></div> * 200
复制代码
body {
height: 100vh;
// 设置渐变背景
background: radial-gradient(ellipse at bottom, #1b2735 0%, #090a0f 100%);
overflow: hidden;
// https://developer.mozilla.org/zh-CN/docs/Web/CSS/filter-function/drop-shadow()
filter: drop-shadow(0 0 10px white);
}
/// 函数 随机生成 $min ~ $max number
@function random_range($min, $max) {
$rand: random();
$random_range: $min + floor($rand * (($max - $min) + 1));
@return $random_range;
}
.snow {
// total number 雪❄️的总量
$total: 200;
position: absolute;
// snow size & shape & style
// 雪❄️的大小形状颜色
width: 10px;
height: 10px;
background: white;
border-radius: 50%;
@for $i from 1 through $total {
// 随机出现的 x 位置
$random-x: random(1000000) * 0.0001vw;
// random offset 随机偏移量
$random-offset: random_range(-100000, 100000) * 0.0001vw;
// 中途摆动的 x 偏移
$random-x-end: $random-x + $random-offset;
// 随机消失的 x 位置
$random-x-end-yoyo: $random-x + ($random-offset / 2);
// 随机摆动时间点(百分比)
$random-yoyo-time: random_range(30000, 80000) / 100000;
// 中途摆动的 y 偏移
$random-yoyo-y: $random-yoyo-time * 100vh;
// 雪❄️的随机大小
$random-scale: random(10000) * 0.0001;
// 随机的下落持续时长
$fall-duration: random_range(10, 30) * 1s;
$fall-delay: random(30) * -1s;
// 设置对映动画 & style
&:nth-child(#{$i}) {
opacity: random(10000) * 0.0001;
transform: translate($random-x, -10px) scale($random-scale);
animation: fall-#{$i} $fall-duration $fall-delay linear infinite;
}
@keyframes fall-#{$i} {
#{percentage($random-yoyo-time)} {
transform: translate($random-x-end, $random-yoyo-y) scale($random-scale);
}
to {
transform: translate($random-x-end-yoyo, 100vh) scale($random-scale);
}
}
}
}
复制代码