こんにちは、うにぽたです。
初Qiita投稿にしてAdc大遅刻です・・・すいません。
CSSメタ言語
CSSはスタイルシートと言うだけあって構造化の点で弱さのある言語です。
大規模開発で複雑化してくるとBEMやFLOCSSなどの設計手法を取り入れることもあるわけですが、メタ言語として問題の改善を図ろうとするアプローチがありました。
メタ言語というのはこの場合最終的にCSSを出力するような、独自の機能拡張を行なっている言語のことです。
Sassもその1つです。
この記事
Sassの具体的なメリットを挙げる前に、この拡張言語には2つの記法が存在することを知ってもらう必要があります。
Sass記法と、SCSS記法です。
実にややこしいですね。
ググラビリティ(Google検索のしやすさ)を大幅に下げている要因です。
Sass記法
Sassの元々の記法は次のようなものでした。
インデントによりネストを表現するPythonライクな文法です。
拡張子は.sass
$font-stack: Helvetica, sans-serif
$primary-color: #333
body
font: 100% $font-stack
color: $primary-color
SCSS記法
CSSとの互換性を重視して生まれた記法です。
CSSのスーパーセットとなっており、上位互換性を持ちます。
つまり既存のCSSをそのままSassに導入することが可能です。
拡張子は.scss
$font-stack: Helvetica, sans-serif;
$primary-color: #333;
body {
font: 100% $font-stack;
color: $primary-color;
}
紹介記事の多さ的には後者のSCSSの方が普及しているようです。
CSSからの移行が簡単なので、初めてAltCSSをやる人はこちらから始めるのもいいかもしれません。
しかし末尾の無駄なセミコロン(!)を省略できることや、インデントでネストを表現できるゆえの編集のし易さは素晴らしく、私はこれなしでCSSを書くことは考えられません。
というわけでこの記事は基本的にSass記法を前提としてSassのシンタックス紹介を行います。
とはいえ少しの読み換えでSCSS使いの人にも参考になるかと思います。
余談ですがSass記法が好きな人はHTMLもPugで書くと良いでしょう。
使い方
Sassコンパイラにはいくつかの実装があり環境に合わせたコンパイル環境が必要なのでここでは割愛です。
とりあえず環境構築せずにお手軽に試したいときはこちらを是非。
-
SassMeister
左側のペインにSassを入力するとコンパイルされたCSSが右側に表示されます。
コンパイル結果がすぐに分かるので、Sassで何が出来るのかをさっと確認できます。
デフォルトでSCSSになっているのでSassに設定を変更しましょう。 -
Codepen
Web上でHTML・CSS・JSを書けるサービスです。
各種プリプロセッサーを設定できるのが強いです。
設定からCSS PreprocessorにSassを選びましょう。
CSS拡張機能
ネスト
Sassでは入れ子構造での記述が可能です。
HTML構造に近い感覚で書けるようになる上、セレクタの記述量が減らせます。
Sass
#main p
color: #00ff00
width: 97%
.redbox
background-color: #ff0000
color: #000000
CSS
#main p {
color: #00ff00;
width: 97%;
}
#main p .redbox {
background-color: #ff0000;
color: #000000;
}
親の参照
ネストの中で親のセレクタを参照することができます。
ホバーなど擬似要素・擬似クラスで使うと非常に可読性が増します。
Sass
a
font-weight: bold
text-decoration: none
&:hover
text-decoration: underline
body.firefox &
font-weight: normal
CSS
a {
font-weight: bold;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
body.firefox a {
font-weight: normal;
}
&
が先頭に来なければいけないという制約はあるものの、こんなこともできます。
Sass
#main
color: black
&-sidebar
border: 1px solid
CSS
#main {
color: black;
}
#main-sidebar {
border: 1px solid;
}
プロパティネスト
CSSのプロパティにはfont-family
,font-size
,font-wight
など「名前空間」を持ったものが多くありますが、Sassにはこれらを楽に書く方法が用意されています。
Sass
.funky
font:
family: fantasy
size: 30em
weight: bold
CSS
.funky {
font-family: fantasy;
font-size: 30em;
font-weight: bold;
}
font
などの「名前空間名」自体のプロパティの場合は、以下のように書きます。
Sass
.funky
font: 20px/24px fantasy
weight: bold
CSS
.funky {
font: 20px/24px fantasy;
font-weight: bold;
}
継承
あるクラスが他のクラスのスタイルを全て含む必要が出てくることはよくあると思います。
そんな時共通部分を1つのクラスにまとめ、差分を別クラスに記述するという運用が取られるかもしれません。
しかしこの場合その複数のクラスを同時に使わなければいけないことを知る必要があります。
そんな時は継承を行いましょう。
Sass
.error
border: 1px #f00
background-color: #fdd
.seriousError
@extend .error
border-width: 3px
CSS
.error, .seriousError {
border: 1px #f00;
background-color: #fdd;
}
.seriousError {
border-width: 3px;
}
継承を前提としたセレクターを書くことができます。
Sass
/* このCSSは継承されるため表示されます */
%message-shared
border: 1px solid #ccc
padding: 10px
color: #333
// このCSSは継承されることがないので表示されません
%equal-heights
display: flex
flex-wrap: wrap
.message
@extend %message-shared
CSS
/* このCSSは継承されるため表示されます */
.message {
border: 1px solid #ccc;
padding: 10px;
color: #333;
}
プレースホルダーセレクター
上の例で使った記号%
はプレースホルダーセレクターと呼ばれます。
Sass
#context a%extreme
color: blue
font-weight: bold
font-size: 2em
.notice
@extend %extreme
CSS
#context a.notice {
color: blue;
font-weight: bold;
font-size: 2em; }
オプショナル
ところでもし継承しようとしたクラスが存在しなかったらどうでしょう?
!optional
を付加することで可能な時のみ継承を行うことが可能です。
a.important
@extend .notice !optional
また次の場合もh1
とa
が衝突するので継承不可能と見なされます。
h1.notice
color: red
a.important
@extend .notice !optional
(もっとも継承が不可能でもエラーが出るわけではないのですが・・・)
コメント
Sassには二種類のコメントアウトがあります。
CSSへコンパイルされて消えるコメントと維持されるコメントです。
というよりCSSに元々ある記法はそのままで、Sass用のコメントが用意されているといった方がいいかもしれません。
Sass
/* このコメントは
* CSSと同じ記法なので
* コンパイル後も残ります
*/
// このコメントはSass拡張です
// コンパイル後は消えます
// 変数の埋め込みもできる
$version: 1.2
/* My Awesome Framework ver.#{$version} */
CSS
/* このコメントは
* CSSと同じ記法なので
* コンパイル後も残ります
*/
/* My Awesome Framework ver.1.2 */
Sassの出力をcompressed
にするとCSSコメントであろうが削除されてしまうのですが、先頭に!
を入れておくと残るので権利表記などにオススメです。
Sass Script
Sassにはコンパイル時に動作するスクリプトを記述することができます。
演算結果をプロパティに使うことも、セレクターを生成することさえもできます。
Sassのデータ型一覧
- 数値
-
1
,1.5
,10px
-
- 文字列
-
"hoge"
,'poyo'
,foo
-
- 色
-
blue
,#04a3f9
,rgb(0,0,0)
,rgba(0,0,0,0.5)
,hsl(0,100%,100%)
,hsla(0,100%,100%,0.5)
,cmyk(100%, 0%, 0%, 0%)
-
- 真偽値
-
true
,false
-
- ヌル値
null
- リスト
- スペースもしくはカンマ区切り
-
1.5em 1em 0 2em
,Helvetica, Arial, sans-serif
- マップ
(key1: value1, key2: value2)
- 関数参照
数値演算
数値に対して各種演算(加減乗除剰余)が可能です。
後述の変数と組み合わせると、値の根拠が示しやすくなります。
計算はSassのコンパイル時に行われるため、表示するまで確定しない値(%やemなどの相対単位)は展開されないことに気をつけてください。
動的な計算を行いたい場合はcalc()を利用しましょう。
.foo1
width: 20 + 5 + px // 25px
.foo2
width: 20px - 5px * 2 // 10px
// 文字列になってしまうので注意
.foo3
width: 20 + 5 + 'px' // "25px"
// 絶対単位同士の計算が可能
.foo4
width: 20px + 1pt + 0.1cm + 1mm // 28.8923884514px
// 相対単位同士の計算はエラー
.foo5
width: 1em + 1%
乗算の注意事項
単位をもつ値同士の乗算は次元が変わってしまうので不可能です。
.hoge
width: 20px * 2px
除算の注意事項
/
が除算として解釈されるのは次の場合のみです。
-
少なくとも片方が値が変数または関数の返り値である
-
()
で囲まれている(ただしリストの()
は無効) -
値が他の演算の一部として扱われている
$x: 2
.hoge1
width: (20px / 4) // 5px
height: 20px / $x // 10px
width: round(10.5)/2 px // 5.5px
// 単位つきの値同士の除算は比になる=単位が出力されない
.hoge2
height: (20px / 2px) // 10
変数を用いる場合で、除算してほしくない場合は#{}
を使いましょう。
Sass
s
p
$font-size: 12px
$line-height: 30px
font: #{$font-size}/#{$line-height}
CSS
p {
font: 12px/30px; }
各種数値関数
-
percentage($number)
- 単位を持たない数値をパーセンテージに変換
-
round($number)
- 数値の小数点を四捨五入した整数を返す
-
ceil($number)
- 数値の小数点を切り上げた整数を返す
-
floor($number)
- 数値の小数点を切り下げた整数を返す
-
abs($number)
- 数値の絶対値を返す
-
min($number...)
- 複数引数を渡す、最小のものを返す
-
max($number...)
- 複数引数を渡し、最大のものを返す
-
random([$number])
- 1~$numberのランダムな整数を返す
- 引数を省略した場合0~1のランダムな実数を返す
変数
プロパティの値を変数に代入して参照することができます。
変数名は$
で始める必要があり、参照前に宣言しなければいけません。
余談ですがCSSの日本語font-familyの設定にはFont-familyメーカーが便利です。
Sass
$font-gothic: YuGothic,'Yu Gothic','Hiragino Kaku Gothic ProN','ヒラギノ角ゴ ProN W3',sans-serif
$primary-color: #111
$main-font-size: 12px
body
font-family: $font-gothic
color: $primary-color
font-size: $main-font-size
CSS
@charset "UTF-8";
body {
font-family: YuGothic, "Yu Gothic", "Hiragino Kaku Gothic ProN", "ヒラギノ角ゴ ProN W3", sans-serif;
color: #111;
font-size: 12px;
}
デフォルト値
同名変数が宣言されていない場合と、nullである場合のみ有効なデフォルト値を設定可能です。
別ファイルで利用している変数を上書きしたくないけど、変数の存在を保証したい場合などに利用できます。
Sass
// nullは未定義として扱われる
$hoge: null
$hoge: "Hello" !default
.foo
content: $hoge
// 宣言済みの変数を上書きすることはない
$poyo: "poyopoyo"
$poyo: "second poyo" !default
.bar
content: $poyo
CSS
.foo {
content: "Hello";
}
.bar {
content: "poyopoyo";
}
文字列
CSSには"poyopoyo"
や'https://example.com'
のような"quoted"な文字列と、sans-serif
やbold
のような"unquoted"な文字列の両方があり、もちろんSassも両方を区別します。
基本的にそのままCSSに反映されますが、埋め込みを行った時のみ例外です。
Sass
$hoge: "poyo"
.root #{$hoge}
color: red
CSS
.root poyo {
color: red;
}
また文字列の結合を行う順序によって出力が変わるので注意です
Sass
p:before
content: "Foo " + Bar
font-family: sans- + "serif"
CSS
p:before {
content: "Foo Bar";
font-family: sans-serif; }
リスト
margin: 10px 15px 0 0
やfont-face: Helvetica, Arial, sans-serif
といったCSSのパラメータを、Sassはリストとして扱います。
リストには上記のようにスペース区切りのものとカンマ区切りの2つがありSass内部では違うものとして見なされます。
リスト要素へのアクセスを含めて、操作は各種関数を通して行います。
リスト定義
$list: 5px 10px 5px 0
.hoge
margin: $list
関連関数
-
nth($list, $n)
- リストの$n番目の要素にアクセスする
-
length($list)
- リストの要素数を返す
-
set-nth($list, $n, $value)
- リストの$n番目に値を代入する
-
join($list1, $list2, [$separator, $bracketed])
- 2つのリストを結合する
-
append($list1, $val, [$separator])
- リストの末尾に値を追加する
マップ
マップは、次のようにキーと値の対応のリストになっています。
$map: (key1: value1, key2: value2, key3: value3);
キーも値もマップを含めたSassのすべてのオブジェクトを使えます。
関連関数
全てのマップ関数は非破壊的、つまり新しいマップを返す関数であることに注意してください。
-
map-get($map, $key)
- キーを元にマップから値を取り出す
-
map-merge($map1, $map2)
- 2つのマップをマージ
-
map-remove($map, $keys…)
- キー(と値)を削除
-
map-keys($map)
- キーのリストを取り出す
-
map-values($map)
- 値のリストを取り出す
-
map-has-key($map, $key)
- キーが値を持っているかを返す
色周りの関数
RGB
rgb($red, $green, $blue)
rgba($red, $green, $blue, $alpha)
rgba($color, $alpha)
-
red($color), green($color), blue($color)
- 各成分の取得
-
mix($color, $color, [$weight])
- 2つの色を混ぜる
- mix(red, blue) => purple
HSL
-
hsl($hue, $saturation, $lightness)
- hue:色相, 0~360
- saturation:彩度, 0~100%
- lightness:輝度, 0~100%
- `hue($color), saturation($color), lightness($color)
- 各成分を取得
-
lighten($color, $amount), darken($color, $amount)
- 輝度を操作
-
saturate($color, $amount), desaturate($color, $amount)
- 彩度を操作
-
grayscale($color)
- グレースケールに変換
-
complement($color)
- 補色を返す
-
invert($color, [$weight])
- 反転色を返す
その他
-
alpha($color)
,opacity($color)
- アルファ値または不透明度を返す
-
opacify($color, $amount)
,fade-in($color, $amount)
- 不透明度を増す
-
transparentize($color, $amount)
,fade-out($color, $amount)
- 透明度を増す
埋め込み(Interpolation)
ここまでの例で見てきたように、変数のセレクタやプロパティ、パラメータへの埋め込みが可能です。
javascriptのテンプレートリテラルのような感じです。
Sass
$name: foo
$attr: border
p.#{$name}
#{$attr}-color: blue
CSS
p.foo {
border-color: blue;
}
@-規則
@import
Sassは@import
により他のSassファイルをインポートし、結合することが可能です。
しかしCSSにも@import
は存在するため、以下の条件によってSassコンパイラの解釈が変わります。
- ファイル拡張子が
.css
- ファイル名が
http(s)://
で始まる - ファイル名が
url()
で記述されている - メディアクエリを持つ
上記に当てはまるとき、以下のようにコンパイルされます。
Sass
@import "foo.css"
@import "foo" screen
@import "http://foo.com/bar"
@import url(foo)
CSS
@import "foo.css";
@import "foo" screen;
@import "http://foo.com/bar";
@import url(foo);
つまりそのままですね。
逆に以下の場合はSassファイルのインポートが行われ、コンパイル後のCSSファイルに@import
は残りません。
@import "foo.sass"
@import "foo"
複数のインポートも可能です。
@import "rounded-corners", "text-shadow"
また、url()
を用いる場合のみ変数の埋め込みが可能です。
$family: unquote("Droid+Sans")
@import url("http://fonts.googleapis.com/css?family=#{$family}")
パーシャル
ファイル名をアンダースコア_
で始めることで、該当ファイルはインポートされるがCSSへ出力されなくなります。
変数のみ使いたい場合や継承のみする場合に有効です。
この場合でもファイル名の扱いはアンダースコアを抜いたもになります。
つまり、_colors.sass
というファイルをインポートするとき、
@import "colors"
とするということです。
結果的に同名となるファイルは同一ディレクトリに共存できません。
インデックス
ファイル名を_index.scss
または_index.sass
とするとファイルのあるディレクトリ名でimportすることが可能です。
ディレクトリ名と同名のファイルが同一階層にある場合は、同じ階層のファイルが優先されます。
ネストされたインポート
セレクタでネストしてインポートすることも可能です。
以下のようなexample.sass
があったとして、
.example {
color: red;
}
このようにインポートすると、
#main {
@import "example";
}
こうなります。
.example {
color: red;
}
@media
Sass
@media screen and (min-width: 720px)
body
font-size: 10px
footer
color: white
CSS
@media screen and (min-width: 720px) {
body {
font-size: 10px;
}
footer {
color: white;
}
}
@keyframes
Sass
ss
@keyframes animation1
from
color: red
to
color: blue
@keyframes animation2
0%
opacity: 0
100%
opacity: 1
CSS
@keyframes animation1 {
from {
color: red;
}
to {
color: blue;
}
}
@keyframes animation2 {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@extend
上の項に書いたので省略です。
@at-root
@debug
@warn
@error
ここら辺も省略します。
気になったらドキュメントを読もう。
ミックスイン
Sassでスタイルを無駄なく(.float-left
のようなクラスを作ることなく)再利用するための手段がミックスインです。
引数を取ることもできるので呼ぶとスタイルを返す特殊な関数のように捉えておくと良いでしょう。
@mixin
で定義を行い、@include
で呼び出した場所にスタイルを埋め込みます。
また、歴史的経緯からミックスインの名前はハイフン-
とアンダースコア_
を区別しないので注意です。
Sass
@mixin large-text
font:
size: 20px
weight: bold
// - と _を区別しない
.main-article
@include large_text
CSS
.main-article {
font-size: 20px;
font-weight: bold;
}
ここでSass記法ユーザーだけの朗報があります。
Sass記法にはミックインのためのスマートな省略記法が存在します。
@mixin
は=
、@include
は+
で代替が可能です。
=large-text
font:
size: 20px
weight: bold
.main-article
+large_text
ミックインで親参照&
を用いることもできます。
Sass
=new-item
display: inline-block
&:before
content: "New!"
.item-box
+new-item
CSS
.item-box {
display: inline-block;
}
.item-box:before {
content: "New!";
}
引数
ミックスイン名の後に引数が代入される変数名を書くことで、呼び出し時に値を渡すことができます。
:
で値を繋げることでデフォルト引数を示すことができます。
またキーワード引数として、変数名を明示して引数を書くことも可能です。
この場合引数の順序を意識する必要がなく、呼び出した側の可読性も高まるのでおすすめです。
Sass
=my-border($color, $width: 5px)
border:
color: $color
width: $width
style: solid
.item-box
+my-border(red, 10px)
// デフォルト引数が設定されてるので省略可
.content-box
+my-border(blue)
// キーワードを指定することで引数の順序が自由に
.article-box
+my-border($width: 3px, $color: green)
CSS
.item-box {
border-color: red;
border-width: 10px;
border-style: solid;
}
.content-box {
border-color: blue;
border-width: 5px;
border-style: solid;
}
.article-box {
border-color: green;
border-width: 3px;
border-style: solid;
}
可変引数
box-shadow
のようにパラメータの数が不定の場合は可変引数によってリストとして受け取るのが良いでしょう。
変数名の後に...
を加えることで複数の引数がリストとして代入されます。
Sass
@mixin box-shadow($shadows...)
-moz-box-shadow: $shadows
-webkit-box-shadow: $shadows
box-shadow: $shadows
.shadows
@include box-shadow(0px 4px 5px #666, 2px 6px 10px #999)
CSS
.shadows {
-moz-box-shadow: 0px 4px 5px #666, 2px 6px 10px #999;
-webkit-box-shadow: 0px 4px 5px #666, 2px 6px 10px #999;
box-shadow: 0px 4px 5px #666, 2px 6px 10px #999;
}
逆にミックスイン呼び出しでリストを展開することもできます。
Sass
@mixin colors($text, $background, $border)
color: $text
background-color: $background
border-color: $border
$values: #ff0000, #00ff00, #0000ff
.primary
@include colors($values...)
CSS
.primary {
color: #ff0000;
background-color: #00ff00;
border-color: #0000ff;
}
ブロック渡し
ミックスインに呼び出し側のスタイルブロックそのものを渡し、@content
の位置に埋め込むことが可能です。
次のようにメディアクエリの再利用がとても便利です。
Sass
=pc-only
@media screen and (max-width: 720px)
@content
+pc-only
.big-text
font-width: 20px
color: red
CSS
@media screen and (max-width: 720px) {
.big-text {
font-width: 20px;
color: red;
}
}
関数
パラメータを生成するのに便利な、単純な関数もあります。
Sass
$grid-width: 40px
$gutter-width: 10px
@function grid-width($n)
@return $n * $grid-width + ($n - 1) * $gutter-width
#sidebar
width: grid-width(5)
CSS
$grid-width: 40px;
$gutter-width: 10px;
@function grid-width($n) {
@return $n * $grid-width + ($n - 1) * $gutter-width;
}
#sidebar { width: grid-width(5); }
制御構文
条件分岐
@if
false
かnull
以外は全て真として扱われます。
Sass
p
@if 1 + 1 == 2
border: 1px solid
@if 5 < 3
border: 2px dotted
@if null
border: 3px double
CSS
p {
border: 1px solid;
}
繰り返し
@for
Sassのfor文には、
for $i from 開始値 through 終了値
for $i from 開始値 to 終了値
の2つがあります。
違いは、前者が終了値を含む一方で後者は含まないということです。
なお開始値が終了値よりも大きい場合はカウンタは1ずつ減少していきます。
Sass
@for $i from 1 through 3
.item-#{$i}
width: 2em * $i
@for $i from 1 to 3
.cell-#{$i}
margin: 10px * $i
CSS
.item-1 {
width: 2em;
}
.item-2 {
width: 4em;
}
.item-3 {
width: 6em;
}
.cell-1 {
margin: 10px;
}
.cell-2 {
margin: 20px;
}
@each
リストもしくはマップから順に値を取り出す構文です。
Sass
@each $animal in puma, sea-slug, egret, salamander
.#{$animal}-icon
background-image: url('/images/#{$animal}.png')
CSS
.puma-icon {
background-image: url('/images/puma.png'); }
.sea-slug-icon {
background-image: url('/images/sea-slug.png'); }
.egret-icon {
background-image: url('/images/egret.png'); }
.salamander-icon {
background-image: url('/images/salamander.png'); }
多次元リストの場合、複数の変数に代入することが可能です。
Sass
@each $animal, $color, $cursor in (puma, black, default),(sea-slug, blue, pointer),(egret, white, move)
.#{$animal}-icon
background-image: url('/images/#{$animal}.png')
border: 2px solid $color
cursor: $cursor
CSS
.puma-icon {
background-image: url('/images/puma.png');
border: 2px solid black;
cursor: default; }
.sea-slug-icon {
background-image: url('/images/sea-slug.png');
border: 2px solid blue;
cursor: pointer; }
.egret-icon {
background-image: url('/images/egret.png');
border: 2px solid white;
cursor: move; }
マップはペア(値の組)のリストとして扱われるため、同じく複数変数で受けることができます。
Sass
@each $header, $size in (h1: 2em, h2: 1.5em, h3: 1.2em)
#{$header}
font-size: $size
CSS
h1 {
font-size: 2em; }
h2 {
font-size: 1.5em; }
h3 {
font-size: 1.2em; }
@while
まず必要にならないと思われるディレクティブですが、条件を満たすまでループを続けます。
Sass
$i: 6
@while $i > 0
.item-#{$i}
width: 2em * $i
$i: $i - 2
CSS
.item-6 {
width: 12em;
}
.item-4 {
width: 8em;
}
.item-2 {
width: 4em;
}
おしまい
ドキュメントの中途半端な翻訳みたいになってしまいましたが、Sass記法での例は少ないので少しでも役に立つと嬉しいです。
次は具体的に嬉しいコード例なんかをまとめられるといいなと思ってます。
参考