LoginSignup
51
41

More than 5 years have passed since last update.

こんにちは、うにぽたです。
初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

また次の場合もh1aが衝突するので継承不可能と見なされます。

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-serifboldのような"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 0font-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

falsenull以外は全て真として扱われます。

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記法での例は少ないので少しでも役に立つと嬉しいです。
次は具体的に嬉しいコード例なんかをまとめられるといいなと思ってます。

参考

51
41
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
51
41