今回はSCSSの応用的な使い方を解説していきます。
ループを使いこなす
forとwhile
以下のようなHTMLを用意します
<div class="text-animation">
<span>A</span>
<span>N</span>
<span>I</span>
<span>M</span>
<span>A</span>
<span>T</span>
<span>I</span>
<span>O</span>
<span>N</span>
</div>
fade-inというkeyframesを作成し、text-animationのspanに設定します。
.text-animation {
span {
opacity:0;
animation-name: fade-in;
animation-duration: 1s;
animation-fill-mode: forwards;
}
}
このままだと要素全てが一度にフェードインしてしまうのでanimation-delayを設定します。
しかし、一つ一つの要素に設定していくのは面倒です。
そういったときにfor文を使用することで効率的に記述することができます。
.text-animation {
span {
opacity:0;
animation-name: fade-in;
animation-duration: 1s;
animation-fill-mode: forwards;
@for $i from 2 through 9 {
&:nth-child(#{$i}) {
animation-delay: #{$i / 10}s;
}
}
}
}
@forの後にまず変数を宣言し、from + 開始時の値を入れます。
次にthrough + 終了時の値を入れることで、その中で変数がthroughの値になるまで処理が継続されます。
(変数の中身は毎ループごとにインクリメントされます)
throughの他にtoというものも使えますが、この場合は終了の値の一つ手前の値になったときに処理が終了します。
似たものでwhile文があります。
.text-animation {
span {
$i: 2;
@while $i <= 9 {
&:nth-child(#{$i}) {
animation-delay: #{$i / 10}s;
}
$i: $i + 1;
}
}
}
まずwhile文の外で変数を宣言します。
次に@while 条件式でループの条件を指定します。
最後にwhile文の中で変数の値を変更します。
変数の値の変更を記述しないと無限ループしてしまうため注意が必要です。
eachで配列の中身を取り出す
ページごとにMV画像を設定する際などに、背景画像のパス以外のスタイルは共通するかと思います。
.page-mv {
background-position: center;
background-size: cover;
}
そして、背景画像をクラス名に応じて変更できるようにしたいとき、個別にスタイルを記述していくのは面倒です。
@eachを使い配列の中身を取り出しスタイルを設定してみます。
まずpagesという変数に配列を代入します。
$pages: 'about', 'works', 'service';
これをeachでループさせ、配列の値に応じたスタイルを設定します。
今回はコンパイル後のCSSファイルと同じディレクトリ内に
- page-mv_about.jpg
- page-mv_works.jpg
- page-mv_service.jpg
があるものとして考えます。
.page-mv {
background-position: center;
background-size: cover;
$pages: 'about', 'works', 'service';
@each $page in pages {
&._#{$page} {
background-image: url('./page-mv_#{$page}.jpg');
}
}
}
このように記述すると、個別に設定せずともスタイルを当てることができ、ページが増えた際なども$pagesの配列に値を追加するだけで簡単に背景画像を設定することができます。
mapからbreakpointを取り出しメディアクエリの記述を簡略化する
変数の型にmap型が存在します。
map型はJSのオブジェクトのようなもので、keyとvalueがセットで一つのプロパティとなったものです。
例を一つあげてみます。
$hogeMap: (
name: 'John',
age: 16
);
今回はこれを利用し、メディアクエリの設定をmapに格納し、好きなときに取り出せるようにしてみます。
以下のようなmapを準備します。
$breakpoints: (
sm: 'screen and (max-width: 767px)',
md: 'screen and (min-width: 768px)',
lg: 'screen and (min-width: 1020px)',
);
引数breakpointを受け取りそれに応じて$breakpointsから値を取り出すmixinを定義してみます。
@mixin mq($breakpoint) {
@media #{map-get($breakpoints,$breakpoint)} {
@content;
}
}
mixin内で@contentを使うと、include時に中にスタイルなどを記述することができます。
値を取り出す際はmap-get関数を使い、第一引数にmap,第二引数にmapのkeyを渡します。
今回はmqの引数で$breakpointを受け取り、それを元に$breakpointsから取り出すkeyを決定しています。
定義したmixinは好きな箇所で使うことができます。
.hoge {
@include mq(sm) {
display: none;
}
}
このようにすることで長いメディアクエリの記述を省略することができ、かつbreakpointsは変更になった際でもmapのvalueを変更することで全ての箇所の変更が完了済むため、非常に便利です。
変数に親要素を代入し、子要素から参照する
以下のようなHTMLがあるとします。
<div class="hoge">
<div class="hoge__item">
<h2 class="hoge__title">Hello!</h2>
</div>
<div class="hoge__item">
<h2 class="hoge__title">Hello!</h2>
</div>
</div>
.hoge__itemをマウスホバーした際に.hoge__titleの色を変更するとき、どのように記述すればよいでしょうか。
おそらく以下のような記述となると思います。
.hoge {
&__item {
background-color: white;
border:1px solid black;
&:hover{
.hoge__title {
color: red;
}
}
}
&__title {
transition: all 0.3s;
color: black;
}
}
しかし親要素が.hogeであることはわかっているため、わざわざ同じ名前を記述せずに省略したいところです。
&を変数$thisに代入し、それを参照する
実はsassの&は変数に代入することができます。
方法は簡単で親要素の直下で変数の値として&を渡すだけです。
.hoge {
$this: &;
&__item {
background-color: white;
border:1px solid black;
&:hover{
.hoge__title {
color: red;
}
}
}
&__title {
transition: all 0.3s;
color: black;
}
}
このようにすると変数$thisには親要素である.hogeという文字列が入ります。
これをクラス名に使うことでいつでも親要素を参照することができるようになります。
.hoge {
$this: &;
&__item {
background-color: white;
border:1px solid black;
&:hover{
#{$this}__title {
color: red;
}
}
}
&__title {
transition: all 0.3s;
color: black;
}
}
&__itemからは.hoge内で宣言された$thisを参照することができますが、外部からは参照することができないため、親要素ごとに$thisを設定しても問題ないです。
これでたとえ親要素のクラス名が変わろうともわざわざ子要素内のクラス名の記述を変更する必要がなくなりました。
今回は変数名をthisとしていますがthatでもparentでもなんでもOKです。
@at-rootを使ってみる
これは僕自身使わない機能なのですが、@at-rootを使うとネストされたセレクタをルートに戻すことができます。
.hoge {
.item {
background-color: red;
}
}
このように記述すると、通常は.hoge .itemという風に.hoge内のitemという要素に対してスタイルが当たります。
@at-rootを使うことで.hoge .itemではなく.itemに直接スタイルを記述したときと同様にすることができます。
.hoge {
@at-root {
.item { // コンパイル後は.hoge .itemではなく.hogeとなる
background-color: red;
}
}
}
.hoge .itemよりも.hogeのみにスタイルを当てる方が、詳細度が下がるためスタイルの上書き等もしやすくなると思います。
安全な変数を実装する
通常、変数はルートで宣言するとグローバル変数となり、どこからでも参照、変更することができます。
これは便利な反面、誤って同じ変数名などを使い値を上書きしてしまう危険性があります。
要素内で宣言した変数の場合、外側からは参照、変更ができないため、安全性を担保することができます。
$var: 100;
.hoge {
$var: 200;
width: #{$var}px; //200
}
.fuga {
width: #{$var}px; //100(グローバル変数を参照しているため)
}
@at-rootの外側で宣言した変数は、@at-rootの内側からも参照することができます。
これらの仕様を使うことで外側からは変更できない変数を使いつつ、詳細度を上げずにスタイル当てることができます。
.hoge {
$color: blue;
@at-root {
.title {
color: $color;
}
}
}
このように使うことで.hoge内で宣言したプライベート変数$colorを使いつつ、.titleの詳細度を上げずにスタイルを当てることができます。
自分がこの機能を使っていない理由としてはBEMを使っている限り意識せずとも詳細度を均一に保つことができるためです。
条件分岐
scssでは@ifを使い条件分岐をすることができます。
$hoge: 10;
@if ($hoge > 9) {
.fuga {
display: none;
}
} @else {
.piyo {
display: none;
}
}
このようにすると変数$hogeの値が9より大きい場合には.fugaが非表示に、そうでなければ.piyoが非表示となります。
引数が存在する時のみ処理を実行する
以下のようなmixinを定義します。
@mixin btn($rounded = null) {
background-color: black;
color: white;
display: inline-block;
padding: 16px 32px;
}
$roundedという引数を取りますが、デフォルトではnullを与えるようにしておきます。
nullをデフォルト引数とする理由はscssの仕様が引数を受け取るmixinの実行時には、デフォルト引数が設定されていない値は必ず渡す必要があるためです。
@ifを使い、引数を渡すとborder-radiusの値として設定されるようにします
@mixin btn($rounded = null) {
background-color: black;
color: white;
display: inline-block;
padding: 16px 32px;
@if ($rounded) {
border-radius: #{$rounded}px;
}
}
このようにすれば引数を渡した際は角丸に、そうでない場合は通常の四角のボタンのスタイルを適用することができます。
以上、SCSSの応用的な使い方でした。
使いこなせばweb制作のスピードが格段に早くなるので、ぜひ取り入れてみてみることをおすすめします。