拜读Alex前辈的scss教学,做个笔记。
建置专案
css资料夹是空的,scss资料夹里面有个style.scss,也是空的。
index.html,body内的程式码。
<div class="tabList"> <header> <ul> <li><a href="javascript:void(0)">A</a></li> <li><a href="javascript:void(0)">B</a></li> <li><a href="javascript:void(0)">C</a></li> <li><a href="javascript:void(0)">D</a></li> <li><a href="javascript:void(0)">E</a></li> </ul> </header> <div> <ul> <li> <h3>Microsoft 微软Surface Pro</h3> <p>原价$41888★狂降一万二!</p> <p>29888</p> </li> <li> <h3>Microsoft 微软Surface Pro</h3> <p>原价$41888★狂降一万二!</p> <p>29888</p> </li> <li> <h3>Microsoft 微软Surface Pro</h3> <p>原价$41888★狂降一万二!</p> <p>29888</p> </li> </ul> </div> </div>
预备工具
Prepros 这套工具可以帮助我们将SCSS转成CSS。
建立编辑SCSS所需的环境
Prepros下载安装完成后,打开软体。
将专案拖曳至Prepros内。
此时,Prepros会持续监控专案的scss状态。
在开发过程中,Prepros不能关闭,不然会无法及时将scss转译成css。
回到专案,开始撰写scss。
巢状(Nesting)
巢状(Nesting)可说是新手接触scss的第一个功能,也是最容易理解的。
style.scss
.tabList { > header { color: red; } > div { color: green; }}
.tabList代表选取tabList class,在.tabList大括号里面的>(大于)代表子代选取器。
所以
> header { color: red; }
表示选取.tabList子代中的header元素,并将其color属性设定为红色。
以此类推,上一段的程式码,表示选取.tabList子代中的header和div元素,并将其color属性设定为红色和绿色。
完成之后,存档。
这时在视窗的右下角会出现这个图示。
表示,成功将scss转译成css。
这时会发现css资料夹多了一个style.css,档名同原本的style.scss。
在开发过程,基本上我们可以专注于style.scss就好,至于style.css,Prepros会自动帮我们产生。
之后要做任何修改,请在style.scss,不要在style.css。
Why? 你都用scss了,干嘛还回头用css。
虽然说浏览器只认css,在css修改没有问题,但既然都用scss,就不用再回css了吧。
注意,请记得在index.html引入style.css。
再往下一层修改其他样式。
.tabList { > header { > ul { list-style: none; font-size: 0; } li { display: inline-block; width: calc(100% / 6); font-size: 16px; } } div { color: red; }}
使用>选取header子代中的ul,以及后代中的li。
至于这段程式码的功能请看参考影片,这篇会着重于scss的开发技巧。
注解
scss的注解有两种方式:/ /跟/* */。
那这两种的差别是,/ /(双斜线)不会被编译成css,换言之,使用/ /(双斜线)的注解,只会scss看到,css看不到。
相反地,/* */会被编译成css,所以css会看到。
//你看不到我!你看不到我!/*多行注解会显示*/
变数(Variable)
上一段程式码中,li的width是使用calc方式算出来的,这是css的语法,所以会直接显示于css,不会转译。
那有没有办法直接在scss算出来结果,再转css。
有,使用变数(Variable)可以做到这点。
既然讲到变数,就表示已经开始涉略到写程式思维了。
简单讲,变数就像一个容器,可以为这个容器取名,里面放的,是我们要运算的值,通常会是数值。
scss宣告变数的方式
$width: 100%;$buttonNumber: 6;
以$为开头,再取变数名称,之后,将值设定给这个变数。
变数$width,它的值为100%;变数$buttonNumber,它的值为6。
接下来就可以利用变数直接在scss中做计算,css所显示的会是计算结果。
scss
width: $width / $buttonNumber;
css
width: 16.66667%;
继承(Extend)
对于写后端程式的开发者而言,继承(extend)是必备的基本观念,但前端人员,尤其是设计师,这个就非常陌生了。
先实作,再解释,会比较容易理解。
将a做成按钮区块。
li { display: inline-block; width: $width / $buttonNumber; font-size: 16px; > a { display: block; width: 100%; height: 20px; line-height: 20px; text-decoration: none; } }
a就会变成按钮区块。
那之后要是需要再次将其他的a变成按钮区块呢?
按照css的做法,肯定是要将所有的样式再写一次,有程式观念的人一定知道,这是非常没有效率的做法,尤其专案愈来愈複杂的情况,会很难维护。
css做不到的,scss可以做到,将重複使用的程式码包起来,之后若有需要,再叫出来即可。
修改上面的程式码。
我们将
display: block;text-decoration: none;
抽出来,这是之后将a做成按钮区块必备的属性设定。
设定一个class(aButton),将属性包起来。
scss
$width: 100%;$buttonNumber: 6;.aButton { display: block; text-decoration: none;}.tabList { > header { > ul { list-style: none; font-size: 0; } li { display: inline-block; width: $width / $buttonNumber; font-size: 16px; > a { width: 100%; height: 20px; line-height: 20px; } } } div { color: red; }}
好了,接下来,使用继承,使a吃到.aButton的设定。
> a { @extend .aButton; width: 100%; height: 20px; line-height: 20px; }
使用@extend,继承的关键字,后面接上要继承的class,如此一来,a就会继承.aButton,并吃到.aButton所有样式设定。
css
.aButton, .tabList > header li > a { display: block; text-decoration: none; }.tabList > header > ul { list-style: none; font-size: 0; }.tabList > header li { display: inline-block; width: 16.66667%; font-size: 16px; }.tabList > header li > a { width: 100%; height: 20px; line-height: 20px; }.tabList div { color: red; }
请看第一段,不只.aButton有吃到设定,a确实也吃到设定。
往后,遇到相同的需求,只需将继承.aButton即可。
但这种命名方式有个缺陷,.aButton的功能是用来被继承的,它并不是html里面的任何元素,却被转译成css,是有点奇怪。
因此,我们可以将.aButton改成%aButton。
%aButton { display: block; text-decoration: none;}
> a { @extend %aButton; width: 100%; height: 20px; line-height: 20px; }
%aButton就不会是class了,只是一个集合样式设定的区块,它的存在就是为了继承。
a一样可以继承%aButton,%aButton也不会被转译成css。
转译后,会发现css已经没有%aButton的蹤影了。
.tabList > header li > a { display: block; text-decoration: none; }
所以,只要有共用的样式设定,就可以使用继承来解决。
函式(Function)
有时,遇到属性的数值设定,像是font-size、padding、margin、line-height。
有经验的设计师都会使用级数,或有规则的运算逻辑去设定数值大小,而不会随便设一个数值。
专案小,每个数值可以手调,那万一专案很大呢?动辄数十页,不就改到疯掉?
既然数值是可以用规则算出来,那我们可以设定一个函式(Function),计算出结果。
假设,我们希望某些特定的元素padding,是使用10px、20px、30px这种级距去设定的。
宣告Function
scss
$basePadding: 10px;@function paddingLevel($count: 1) { @return $basePadding * $count;}
首先在最上层,先设定基準padding:10px。
使用@function关键字,宣告一个名为paddingLevel的function。
名称后面的小括号($count: 1),会放入级距等级,变数$count预设是1。
意思是,假使我们都没放任何数值,$count就会以1计算,若放入2,就会以2去计算。
大括号里面,@return关键字,表示会将运算结果回传,讲白话,会将结果给丢出来。
$basePadding * $count,是运算部分,我们放入2,结果就会是10*2,@return就会将20px,回传。
使用function
scss
.a { padding: paddingLevel();}.b { padding: paddingLevel(2);}.c { padding: paddingLevel(3);}
假设我们有不同的class,要套用不同级数的padding,可以输入Function名称(等级)。
css就会像这样。
.a { padding: 10px; }.b { padding: 20px; }.c { padding: 30px; }
传入2个以上数值
刚刚的Function,只能传入一个数值,那如果想要把所有要算的值全部包含在Function里,也是可以的。
@function paddingLevel($count: 1, $basePadding: 10px) { @return $basePadding * $count;}
小括号( ),多了一个参数$basePadding,预设值为10px,并用逗号( , )跟$count做区隔。
scss
.a { padding: paddingLevel();}.b { padding: paddingLevel(2,20px);}.c { padding: paddingLevel(3,30px);}
css
.a { padding: 10px; }.b { padding: 40px; }.c { padding: 90px; }
结果应该很容易理解吧。
以上讲到的,巢状(Nesting)、变数(Variable)、继承(Extend)、函式(Function),算是最基础的功能了。
接下来要讲的mixin,算是整合这些的应用,也比较进阶。
mixin
先看mixin跟extend的差异。
scss
%aButton { display: block; text-decoration: none;}@mixin aButton2 { display: block; text-decoration: none;}.a { @extend %aButton;}.b { @extend %aButton;}.c { @extend %aButton;}.d { @include aButton2();}.e { @include aButton2();}.f { @include aButton2();}
使用@mixin关键字,将共用样式给包起来。
看起来似乎跟extend有点像。
如若要套用mixin的话,要使用@include关键字。
css
.a, .b, .c { display: block; text-decoration: none; }.d { display: block; text-decoration: none; }.e { display: block; text-decoration: none; }.f { display: block; text-decoration: none; }
很明显的区别,虽然达到的目的都一样。
extend是共用一份样式。
但mixin会产生好几份同样的样式。
由此可知,这显然不是mixin该做的事。
那mixin的功能究竟是什么?
可以把mixin想像成,由变数(Variable)、继承(Extend)、函式(Function),所集合而成的大补帖。
恩,听起来笼统,直接实作。
我们想要设定字体大小从12px开始,每个级距是4px。
意思就是字体从12px、16px、20px这样加大的。
scss
$baseSize: 12px;$sizeLevel: 4px;@function font($level: 0) { @if $level < 0 { $level: 0; } @return $baseSize + $sizeLevel * round($level);}
@if是判断式关键字,意思是当$level小于0的时候,就把$level设为0,也就是预防输入负数的情况发生。
回传的是,基本字体大小(12px)+字体级距(4px)*四捨五入(输入的等级)。
round()是四捨五入的函式,回传整数。
scss
.a { font-size: font(5);//12+5*4}.b { font-size: font(6);//12+6*4}.c { font-size: font(7);//12+7*4}
css
.a { font-size: 32px; }.b { font-size: 36px; }.c { font-size: 40px; }
字体大小的级距规则订好了,那行高呢?
不同的字体大小也有相对应的行高,再次设定行高的计算规则。
scss
$baseLineSize: 10px;$paddingLevel: 1.2;@function rhythm($size) { @return ceil($size * $paddingLevel / $baseLineSize) * $baseLineSize;}
$size是字体大小,ceil()会回传大于或等于所给定数值的最小整数。
处理逻辑是这样的:
ceil(字体大小 *1.2 / 10px)* 10px。
scss
.a { font-size: font(5);//12+5*4 line-height: rhythm(font(5));//ceil(32*1.2/10)*10}.b { font-size: font(6);//12+6*4 line-height: rhythm(font(6));//ceil(36*1.2/10)*10}.c { font-size: font(7);//12+7*4 line-height: rhythm(font(7));//ceil(40*1.2/10)*10}
css
.a { font-size: 32px; line-height: 40px; }.b { font-size: 36px; line-height: 50px; }.c { font-size: 40px; line-height: 50px; }
只要使用function,输入数值,就可以得到相对应的结果。
不过,要得到字体大小与行高,得需要2个function,那有没有更简便的方式,只要输入级距,就可以自动产字体大小与行高呢?
有,就是mixin。
@mixin font($level: 1, $line-height: auto) { $size: font($level); $line: rhythm($size); font-size: $size; @if $line-height == auto or $line-height < $line { line-height: $line; } @else { line-height: $line-height; }}
一样使用@mixin关键字,宣告mixin,取名font,有两个参数。
$level预设为1,$line-height预设为auto。
$size: font($level),表示呼叫font() function,把计算结果设定给$size变数。
$line: rhythm($size),同理。
font-size: $size,设定字体大小的属性值为$size变数的值。
@if这段表示,如果$line-height等于auto或者我们自行输入的$line-height小于$line的值的话,就把$line的值设定给line-height属性,不然line-height属性值就使用我们自己输入的值。
@if判断式,对于没有学过程式逻辑的人来说,确实是个门槛,不过scss的强大与效率,是值得投资的。
完整的scss程式码
$baseSize: 12px;$baseLineSize: 10px;$sizeLevel: 4px;$paddingLevel: 1.2;@mixin aButton { display: block; text-decoration: none;}@function font($level: 0) { @if $level < 0 { $level: 0; } @return $baseSize + $sizeLevel * round($level);}@function rhythm($size) { @return ceil($size * $paddingLevel / $baseLineSize) * $baseLineSize;}@mixin font($level: 1, $line-height: auto) { $size: font($level); $line: rhythm($size); font-size: $size; @if $line-height == auto or $line-height < $line { line-height: $line; } @else { line-height: $line-height; }}.a { @include aButton(); @include font(5);}.b { @include aButton(); @include font(6);}.c { @include aButton(); @include font(7);}
所产生的css
.a { display: block; text-decoration: none; font-size: 32px; line-height: 40px; }.b { display: block; text-decoration: none; font-size: 36px; line-height: 50px; }.c { display: block; text-decoration: none; font-size: 40px; line-height: 50px; }
看到了吗?只要一行mixin,就可以产生font-size与line-height。
这就是mixin真正的价值所在。
接下来,我们要将这些function与mixin独立出来,让专案的其他scss或其他专案也可以使用。
首先,在scss资料夹新增_function.scss。
加底线的原因是,这个档案就不会被转译成css,_function.scss的功能只是做运算,不需要转成css。
_function.scss
$baseSize: 12px;$baseLineSize: 10px;$sizeLevel: 4px;$paddingLevel: 1.2;@mixin aButton { display: block; text-decoration: none;}@function font($level: 0) { @if $level < 0 { $level: 0; } @return $baseSize + $sizeLevel * round($level);}@function rhythm($size) { @return ceil($size * $paddingLevel / $baseLineSize) * $baseLineSize;}@mixin font($level: 1, $line-height: auto) { $size: font($level); $line: rhythm($size); font-size: $size; @if $line-height == auto or $line-height < $line { line-height: $line; } @else { line-height: $line-height; }}
里面只有处理逻辑与设定属性值,没有指定任何元素。
只需要在需要计算出字体大小与行高的scss引入_function.scss即可。
test.scss
@import "function";.a { @include aButton(); @include font(5);}.b { @include aButton(); @include font(6);}.c { @include aButton(); @include font(7);}
给初学者的建议,若觉得mixin太複杂,可以先使用变数(Variable)、继承(Extend)、函式(Function)就好。
等到熟悉了,再将这些功能整合进mixin即可。
SCSS还有很多地方可以讲,之后有机会再慢慢补充。
参考来源
[ Alex 宅干嘛 ] ??从 CSS 到 SASS (SCSS) 超入门观念引导
Demo
MyGitHub