Sassの基本的な書き方をまとめてみた

Blog

はじめまして。今年の1月に入社したエンジニアのuchimuroです。
前職ではwebデザイナー兼コーダーとして勤務していたのですが、更にコーディング道を極めたいと思い、エンジニアとしてエヴォワークスに入社しました。

入社〜現在までの2ヶ月間は、すでに公開されているサイトの更新作業を主に担当しておりました。
更新作業ということで先輩方が以前に書いたコードを元に作業を進めていくことが多かったのですが、特にSassファイルの記述を読み解くのに苦戦してしまい、時間をロスしてしまうことが多々ありました。

前職でもSassを使ってはいたのですが、これは改めてイチから学びなおす必要がありそうだと痛感したため、最近はSassについて色々と勉強しております。

というわけで、1回目のブログは自分自身の備忘録も兼ねてSassの基本的な書き方をまとめてみようと思います。
(今回はSCSS記法に則って記述を進めていきます。)

ネスト(入れ子)

  • ネストして記述することで、親セレクタを頭につけてCSSを出力できる。

■ 記述方法

.hoge1 {
    プロパティ: 値;

    .hoge2 {
        プロパティ: 値;

        .hoge3 {
            プロパティ: 値;
        }
    }
}

■ 具体例

.item1 {
    width: 200px;

    .item2 {
        width: 100px;

        .item3 {
            width: 50px;
        }
    }
}

▼ 出力結果 ▼

.item1 {
    width: 200px;
}

.item1 .item2 {
    width: 100px;
}

.item1 .item2 .item3 {
    width: 50px;
}

&(親参照セレクタ)

  • &を記述した部分に親セレクタを自動で付与してCSSに出力できる。

■ 記述方法

.hoge {
    プロパティ: 値;
    &:hover {
        プロパティ: 値;
    }
}

■ 具体例

.item1 {
    width: 100px;
    &:hover {
        width: 50px;
    }
}

.box {
    &-a {
        background: #ffffff;
    }
    &-b {
        background: #000000;
    }
    // 「&」はこのようにセレクタ名の一部としても使用可能
}

▼ 出力結果 ▼

.item1 {
    width: 100px;
}

.item1:hover {
    width: 50px;
}

.box-a {
    background: #ffffff;
}

.box-b {
    background: #000000;
}

// 「&」で親セレクタを参照した場合は
// .box .box-a {}
// のようには出力されないので注意が必要。
// もし上記のように出力させたい場合は
// 「&」の前に、「&」+半角スペースを記述する。

変数

  • 繰り返し使いたい値を管理することができる。

■ 記述方法

$変数名: 値;

■ 具体例

$black: #000000;
$position: left;

.item1 {
    color: $black;
}

.item2 {
    background: $black;
    margin-#{$position}: 30px;
    // #{$変数名}と記述することで、その変数の値を文字列として使用できる。
}

▼ 出力結果 ▼

.item1 {
    color: #000000;
}

.item2 {
    background: #000000;
    margin-left: 30px;
}

マップ変数

  • キーと値のペアを格納できるオブジェクトであり、対応するキーで値を取得することができる。

■ 記述方法

$マップ変数名: (
    キー名1: 値1,
    キー名2: 値2,
    キー名3: 値3
);

■ 具体例

$colors: (
    red: #ff0000,
    green: #00ff00,
    blue: #0000ff
);

.item1 {
    background: map-get($colors, red);
}

.item2 {
    background: map-get($colors, green);
}

// map-get()関数を用いると、
// マップ変数内の指定したキーに対応する値を取得できる。
// マップ変数を操作できる関数は他にも複数存在する。

▼ 出力結果 ▼

.item1 {
    background: #ff0000;
}

.item2 {
    background: #00ff00;
}

配列

  • 複数の値を格納できるオブジェクトを定義する。

■ 記述方法

$配列名: 値1, 値2, 値3;

■ 具体例

$colors: #ff0000, #00ff00, #0000ff;

.item1 {
    background: nth($colors, 2);
    // nth()関数を用いると、
    // 配列内の指定したインデックス番号の要素を取得できる。
    // 「0」始まりではなく、「1」始まりなので注意が必要。
}

.item#{length($colors)} {
// length()関数を用いると、
// 配列内の要素数を取得できる。
    background: nth($colors, 1);
}

▼ 出力結果 ▼

.item1 {
    background: #00ff00;
}

.item3 {
    background: #ff0000;
}

@mixin & @include

  • 繰り返し使いたいスタイルやルールセットを管理することができる。

■ 記述方法

【 引数を指定しない場合 】

@mixin mixin名 {
    プロパティ: 値;
}

.hoge {
    @include mixin名;
}

【 引数を指定する場合(初期値なし) 】

@mixin mixin名($引数名1, $引数名2) {
    プロパティ: $引数名1;
    プロパティ: $引数名2;
}

.hoge {
    @include mixin名(引数1, 引数2);
}

【 引数を指定する場合(初期値あり) 】

@mixin mixin名($引数名1: 初期値, $引数名2: 初期値) {
    プロパティ: $引数名1;
    プロパティ: $引数名2;
}

.hoge {
    @include mixin名();
    // ここで引数を設定しない場合は初期値に設定した値が適用される。
}

【 @contentを使用する場合 】

@mixin mixin名 {
    @content;
}

.hoge {
    @include mixin名 {
        // ここに記述した内容は@contentの部分に記載されていることになる。
    }
}

■ 具体例

@mixin text1 {
    color: #ff0000;
    font-size: 18px;
}

@mixin text2($size, $weight) {
    font-size: $size;
    font-weight: $weight; 
}

@mixin text3($size: 20px, $weight: normal) {
    font-size: $size;
    font-weight: $weight; 
}

// mixinの中にはルールセットを入れることも可能
@mixin box {
    .box {
        background: #ff0000;
        padding: 10px;
    }
}

@mixin btn {
    .btn {
        height: 100px;
        width: 300px;
        @content;
    }
}

.item1 {
    @include text1;
}

.item2 {
    @include text2(24px, bold);
}

.item3 {
    @include text3(24px, bold);
}

.item4 {
    @include text3();
}

.item5 {
    width: 100px;
    @include box;
}

.item6 {
    @include btn {
        background: #ff0000;
        color: #ffffff;
    }
}

▼ 出力結果 ▼

.item1 {
    color: #ff0000;
    font-size: 18px;
}

.item2 {
    font-size: 24px;
    font-weight: bold; 
}

.item3 {
    font-size: 24px;
    font-weight: bold; 
}

.item4 {
    font-size: 20px;
    font-weight: normal; 
}

.item5 {
    width: 100px;
}

.item5 .box {
    background: #ff0000;
    padding: 10px;
}

.item6 .btn {
    height: 100px;
    width: 300px;
    background: #ff0000;
    color: #ffffff;
}

@extend & プレースホルダー

  • 一度定義したスタイルを他のセレクタでも使用できる。

■ 記述方法

【 通常のセレクタからextendする場合 】

.hoge1 {
    プロパティ: 値;
}

.hoge2 {
    @extend .hoge1;
}

【 プレースホルダーセレクタからextendする場合 】

//プレースホルダーセレクタを指定する際は頭に「%」を記載する。

%hoge1 {
    プロパティ: 値;
}

.hoge2 {
    @extend %hoge1;
}

■ 具体例

.item1 {
    margin: 0 auto;
    padding-top: 30px;
}

%btn {
    height: 80px;
    width: 200px;
}

.item2 {
    @extend .item1;
    width: 100px;
}

.item3 {
    @extend %btn;
}

▼ 出力結果 ▼

.item1, .item2 {
    margin: 0 auto;
    padding-top: 30px;
}

.item3 {
    height: 80px;
    width: 200px;
}

.item2 {
    width: 100px;
}

// プレースホルダーセレクタはcssには出力されない。

@at-root

  • ネストしているセレクタをネストしていない状態(ルート)に戻す。

■ 記述方法

.hoge1 {
    プロパティ: 値;

    .hoge2 {
        プロパティ: 値;

        @at-root .hoge3 {
            プロパティ: 値;
        }
    }
}

■ 具体例

.item1 {
    margin-top: 10px;

    .item2 {
        margin-right: 10px;

        .item3 {
            margin-bottom: 10px;

            @at-root .item4 {
                margin-left: 10px;
            }
        }
    }
}

▼ 出力結果 ▼

.item1 {
    margin-top: 10px;
}

.item1 .item2 {
    margin-right: 10px;
}

.item1 .item2 .item3 {
    margin-bottom: 10px;
}

.item4 {
    margin-left: 10px;
}

@if

  • 指定した条件に合致する場合にのみ、処理を実行する。

■ 記述方法

【 条件を1つのみ指定する場合 】

@if 条件 {
    // 条件が合致したら、この中の処理を実行する。
} @else {
    // 条件が合致しない場合は、この中の処理を実行する。
}

// @elseより後ろは省略も可能

【 条件を複数指定する場合 】

@if 条件1 {
    // 条件1が合致したら、この中の処理を実行する。
} @else if 条件2 {
    // 条件2が合致したら、この中の処理を実行する。
} @else {
    // どの条件も合致しない場合は、この中の処理を実行する。
}

■ 具体例

$position: left;
$width: 250px;

.item1 {
    @if $position == left {
        padding-left: 10px;
    }
}

.item2 {
    @if $width > 300px {
        margin-right: 10px;
    } @else if $width < 200px {
        margin-left: 10px;
    } @else {
        margin-top: 10px;
    }
}

▼ 出力結果 ▼

.item1 {
    padding-left: 10px;
}

.item2 {
    margin-top: 10px;
}

@for

  • 開始値と終了値を設定して、繰り返し処理を実行する。

■ 記述方法

【 開始値〜終了値の間、処理を実行する場合 】

@for $変数 from 開始値 through 終了値 {
    //変数が開始値〜終了値の間、この中の処理を実行する。
}

【 開始値〜終了値未満の間、処理を実行する場合 】

@for $変数 from 開始値 to 終了値 {
    //変数が開始値〜終了値未満の間、この中の処理を実行する。
}

■ 具体例

@for $i from 1 through 3 {
    .item#{$i} {
        margin-right: 10px * $i;
    }
}

@for $i from 1 to 3 {
    .box#{$i + 5} {
        width: (100px * $i) + 10px;
    }
}

▼ 出力結果 ▼

.item1 {
    margin-right: 10px;
}

.item2 {
    margin-right: 20px;
}

.item3 {
    margin-right: 30px;
}

.box6 {
    width: 110px;
}

.box7 {
    width: 210px;
}

@while

  • 指定した条件に合致している間、繰り返し処理を実行する。

■ 記述方法

@while 条件 {
    // 条件が合致している間、この中の処理を繰り返し実行する。
}

■ 具体例

$i: 1;

@while $i < 4 {
    .item#{$i} {
        height: 30px * $i;
    }
    $i: $i + 1;
}

▼ 出力結果 ▼

.item1 {
    height: 30px;
}

.item2 {
    height: 60px;
}

.item3 {
    height: 90px;
}

@each

  • マップ変数や配列内の要素数の分、繰り返し処理を実行する。

■ 記述方法

@each $変数 in $マップ変数(配列) {
    // マップ変数(配列)の要素数の分、この中の処理を実行する。
}

■ 具体例

$positions: top, right, bottom, left;
$colors: (
    red: #ff0000,
    green: #00ff00,
    blue: #0000ff
);

@each $n in $positions {
    .box#{index($positions, $n)} {
    // index()関数を用いると、
    // その要素が配列内の何番目にいるかを取得できる。
        margin-#{$n}: 20px;
    }
}

@each $p, $v in $colors {
// マップ変数で処理を行う場合は変数を2つ記述する必要がある。
// (「キー」とそれに対応する「値」を格納するため。)
    .item-#{$p} {
        background: $v;
    }
}

▼ 出力結果 ▼

.box1 {
    margin-top: 20px;
}

.box2 {
    margin-right: 20px;
}

.box3 {
    margin-bottom: 20px;
}

.box4 {
    margin-left: 20px;
}

.item-red {
    background: #ff0000;
}

.item-green {
    background: #00ff00;
}

.item-blue {
    background: #0000ff;
}

@function

  • @functionで関数を定義して、@returnで値を返す。

■ 記述方法

@function 関数名($引数名) {
    @return 戻り値;
}

// 引数には@mixin同様に初期値を設定することもできる。

■ 具体例

@function half($size) {
    @return $size / 2;
}

.item {
    width: half(20px);
}

▼ 出力結果 ▼

.item {
    width: 10px;
}

番外編 その1

  • 引数を指定しない@mixinとプレースホルダーセレクタの違い

■ 具体例

@mixin btn {
    height: 80px;
    width: 200px;
}

%btn {
    height: 80px;
    width: 200px;
}

.item1 {
    @include btn;
}

.item2 {
    @include btn;
}

.item3 {
    @extend %btn;
}

.item4 {
    @extend %btn;
}

▼ 出力結果 ▼

.item4, .item3 {
    height: 80px;
    width: 200px;
}

.item1 {
    height: 80px;
    width: 200px;
}

.item2 {
    height: 80px;
    width: 200px;
}

// 引数を指定しない@mixinとプレースホルダーセレクタには
// ・スタイルを繰り返し使用することができる。
// ・コンパイル後のcssには出力されない。
// という共通点があるが、上記を見るとわかるように
// 出力後のセレクタがグループ化されるか否かという違いがある。

番外編 その2

  • よく使われるメディアクエリの設定方法

■ 具体例

@mixin tb {
	@media screen and (min-width: 768px) {
		@content;
	}
}

@mixin pc {
	@media screen and (min-width: 1280px) {
		@content;
	}
}

.item {
    margin: 30px auto;
    text-align: right;

    @include tb {
        margin: 0;
        text-align: center;
    }

    @include pc {
        margin: 100px;
        text-align: left;
    }
}

▼ 出力結果 ▼

.item {
    margin: 30px auto;
    text-align: right;
}

@media screen and (min-width: 768px) {
    .item {
        margin: 0;
        text-align: center;
    }
}

@media screen and (min-width: 1280px) {
    .item {
        margin: 100px;
        text-align: left;
    }
}

おわりに

長々となりましたが、Sassの書き方について色々とまとめてみました。
Sassを実際の案件で使用する時は、css設計・ファイル構成との兼ね合いや様々な機能を組み合わせたりする必要があるため、今回まとめた内容を理解できていることに加えて、より実戦的な知識や経験も求められます。
ですので自分も引き続き勉強を続けつつ、案件でバリバリにSassを書いて、更に経験を蓄えていこうと思います。

Sassにはここでまとめきれなかった機能や書き方がまだまだ沢山あるので、興味のある方は調べてみてください。

それでは今回はここらへんで失礼します。

参考