Sass入門|変数・ネスト・ミックスイン・関数でCSSをスマートに書く


はじめに

前回の記事ではJavaScriptでカルーセルと背景ぼかしを実装する方法を解説しました。今回は**Sass(サス)**の基本的な使い方を解説します。「CSSが長くなってきて管理しにくい」「同じ色コードを何度も書いている」「ネストした要素のセレクターを毎回フルパスで書くのが面倒」という悩みをSassは一気に解決してくれます。変数・ネスト・ミックスイン・関数を使いこなすことで、CSSが格段に書きやすく・読みやすく・メンテナンスしやすくなります。


1. Sassとは何か

1.1 Sassの概要

Sass(Syntactically Awesome Style Sheets):

CSSを拡張したメタ言語。
Sassで書いたコードを
コンパイル(変換)することで
通常のCSSが生成される。

ブラウザはSassを直接読めないため
必ずコンパイルが必要。

Sassの2つの記法:

① SCSS記法(推奨):
   → CSSとほぼ同じ書き方
   → ファイルの拡張子は .scss
   → 既存のCSSをそのまま使える

② Sass記法(インデント記法):
   → インデントでブロックを表現する
   → 波カッコ・セミコロンが不要
   → ファイルの拡張子は .sass

→ 現代のプロジェクトではほぼSCSS記法が使われる
→ この記事もSCSS記法で解説する

1.2 Sassを使うメリット

Sassを使うメリット:

① 変数で色・サイズを一元管理できる
   → 色を変えたいときに1箇所直すだけでOK

② ネストでセレクターを入れ子にできる
   → HTML構造に合わせてCSSを整理できる

③ ミックスインでCSSのまとまりを再利用できる
   → 同じプロパティ群を何度でも使い回せる

④ ファイルを分割して管理できる
   → コンポーネントごとにファイルを分けられる

⑤ 計算・関数が使える
   → 動的な値を計算して生成できる

⑥ ReactやViteと相性がよい
   → モダンな開発環境にシームレスに組み込める

2. Sassの環境を準備する

2.1 Viteプロジェクトに導入する(推奨)

bash

# Viteプロジェクトを作成する
npm create vite@latest my-project -- --template vanilla

cd my-project

# Sassをインストールする
npm install -D sass

# 開発サーバーを起動する
npm run dev
インストール後の使い方:

① src/style.scss を作成する(または .css を .scss に変更)

② main.js で .scss ファイルを import する
   import './style.scss'

③ ViteがSCSSを自動でコンパイルしてくれる
   → 設定不要ですぐに使える

2.2 VSCodeで使う(簡単な方法)

VSCodeの拡張機能「Live Sass Compiler」を使う:

① VSCodeの拡張機能で
  「Live Sass Compiler」を検索してインストール

② .scss ファイルを作成する

③ ステータスバーの「Watch Sass」をクリックする

④ .scss を保存するたびに
  自動的に .css ファイルが生成される

→ HTMLには生成された .css ファイルを読み込む

2.3 npmのsassコマンドで使う

bash

# scss を css にコンパイルする(1回だけ)
npx sass src/style.scss dist/style.css

# ファイルの変更を監視して自動コンパイルする
npx sass --watch src/style.scss dist/style.css

# 圧縮して出力する
npx sass --style=compressed src/style.scss dist/style.min.css

3. 変数(Variables)

3.1 変数の基本

scss

// Sass の変数は $ で始まる
// CSS変数(--color-primary)とは別物

// ============================================
// 変数の定義
// ============================================

// カラー
$color-primary:    #1A5FC0;
$color-secondary:  #27AE60;
$color-accent:     #EF9F27;
$color-text:       #111827;
$color-text-sub:   #6B7280;
$color-bg:         #FFFFFF;
$color-bg-sub:     #F9FAFB;
$color-border:     #E5E7EB;

// フォントサイズ
$font-size-xs:   1.2rem;
$font-size-sm:   1.4rem;
$font-size-base: 1.6rem;
$font-size-lg:   1.8rem;
$font-size-xl:   2.0rem;
$font-size-2xl:  2.4rem;
$font-size-3xl:  3.2rem;
$font-size-4xl:  4.0rem;

// スペーシング
$space-1:  4px;
$space-2:  8px;
$space-3: 12px;
$space-4: 16px;
$space-5: 20px;
$space-6: 24px;
$space-8: 32px;
$space-10: 40px;
$space-12: 48px;
$space-16: 64px;
$space-24: 96px;

// ブレークポイント
$bp-sm:  640px;
$bp-md:  768px;
$bp-lg: 1024px;
$bp-xl: 1280px;

// その他
$radius-sm:  4px;
$radius-md:  8px;
$radius-lg: 12px;
$radius-xl: 16px;
$radius-full: 9999px;

$shadow-sm: 0 1px 3px rgba( 0, 0, 0, 0.08 );
$shadow-md: 0 4px 12px rgba( 0, 0, 0, 0.1 );
$shadow-lg: 0 8px 32px rgba( 0, 0, 0, 0.12 );

$transition-base: 0.2s ease;
$transition-slow: 0.4s ease;

3.2 変数を使う

// 変数を使ってスタイルを書く
.btn {
    background-color: $color-primary;
    color:            $color-bg;
    font-size:        $font-size-base;
    padding:          $space-3 $space-6;
    border-radius:    $radius-md;
    transition:       background-color $transition-base;

    &:hover {
        background-color: darken( $color-primary, 10% );
        // → darken()はSassの組み込み関数
        //   色を指定した%分暗くする
    }
}

.card {
    background:    $color-bg;
    border:        1px solid $color-border;
    border-radius: $radius-lg;
    padding:       $space-6;
    box-shadow:    $shadow-md;
}

// 変数を変えるだけでサイト全体の色が変わる
// → デザイン変更時の修正コストが激減する

💡 SassのI変数とCSS変数(カスタムプロパティ)は使い分けが大切です: Sassの変数はコンパイル時に値に置き換えられます。CSS変数(--color-primary)はブラウザでリアルタイムに変更できます。ダークモードの切り替えなど動的に変える必要があるものはCSS変数・静的な設定値はSassの変数が向いています。


4. ネスト(Nesting)

4.1 ネストの基本

// ❌ 通常のCSSの書き方(冗長)
.nav { display: flex; }
.nav a { color: #111; }
.nav a:hover { color: #1A5FC0; }
.nav .nav-item { padding: 0 16px; }
.nav .nav-item.is-active { font-weight: bold; }

// ✅ Sassのネストで整理する
.nav {
    display: flex;

    a {
        color:      #111;
        text-decoration: none;

        &:hover {   // & = 親セレクター(.nav a:hover)
            color: #1A5FC0;
        }
    }

    .nav-item {
        padding: 0 16px;

        &.is-active { // .nav-item.is-active
            font-weight: 700;
            color: $color-primary;
        }
    }
}

4.2 &(アンパサンド)の使い方

/*
  & は親セレクターを参照する特別な記号。
  擬似クラス・擬似要素・修飾クラスに使う。
*/

.btn {
    background: $color-primary;
    color: #fff;
    transition: background $transition-base;

    // 擬似クラス
    &:hover   { background: darken( $color-primary, 8% ); }
    &:focus   { outline: 2px solid $color-primary; }
    &:active  { transform: translateY( 1px ); }
    &:disabled {
        background: $color-border;
        cursor: not-allowed;
    }

    // 修飾クラス(バリアント)
    &--secondary {
        // .btn--secondary(BEM記法との相性がよい)
        background: $color-secondary;
    }

    &--outline {
        // .btn--outline
        background: transparent;
        border: 2px solid $color-primary;
        color: $color-primary;
    }

    &--sm {
        // .btn--sm
        font-size: $font-size-sm;
        padding:   $space-2 $space-4;
    }

    &--lg {
        // .btn--lg
        font-size: $font-size-lg;
        padding:   $space-4 $space-8;
    }

    // 擬似要素
    &::before {
        content: '';
        display: block;
    }

    // 親セレクターを後ろに置く(コンテキスト変更)
    .dark-theme & {
        // .dark-theme .btn
        background: lighten( $color-primary, 15% );
    }
}

4.3 ネストの注意点

/*
  ❌ 深くネストしすぎるのは避ける
  → 生成されるCSSが複雑になる
  → 詳細度が上がりすぎて上書きしにくくなる
*/

// ❌ 深すぎるネスト(3階層以下を推奨)
.card {
    .card-body {
        .card-title {
            span {      // ← 4階層目は避ける
                em {    // ← 5階層目は絶対NG
                    color: red;
                }
            }
        }
    }
}

// ✅ 適切な深さ(2〜3階層まで)
.card {
    &-body   { padding: $space-6; }
    &-title  { font-size: $font-size-xl; }
    &-text   { color: $color-text-sub; }
    &-footer { padding-top: $space-4; }
}

5. ミックスイン(Mixins)

5.1 ミックスインの基本

/*
  ミックスイン:
  CSSのまとまりを定義して何度でも再利用できる仕組み。
  @mixin で定義して @include で呼び出す。
*/

// ミックスインの定義
@mixin flex-center {
    display:         flex;
    align-items:     center;
    justify-content: center;
}

@mixin flex-between {
    display:         flex;
    align-items:     center;
    justify-content: space-between;
}

// ミックスインの使用
.hero {
    @include flex-center;
    height: 100vh;
}

.header {
    @include flex-between;
    padding: 0 $space-10;
    height:  80px;
}

5.2 引数付きミックスイン

// 引数を受け取るミックスイン
@mixin button-variant( $bg, $color: #fff, $hover-bg: darken( $bg, 10% ) ) {
    background-color: $bg;
    color:            $color;

    &:hover {
        background-color: $hover-bg;
    }
}

// 使用例
.btn-primary   { @include button-variant( $color-primary ); }
.btn-secondary { @include button-variant( $color-secondary ); }
.btn-danger    { @include button-variant( #E24B4A ); }
.btn-dark      { @include button-variant( #111827, #fff ); }

// ============================================

// メディアクエリのミックスイン
@mixin mq( $breakpoint ) {
    @if $breakpoint == 'sm' {
        @media ( min-width: $bp-sm ) { @content; }
    } @else if $breakpoint == 'md' {
        @media ( min-width: $bp-md ) { @content; }
    } @else if $breakpoint == 'lg' {
        @media ( min-width: $bp-lg ) { @content; }
    } @else if $breakpoint == 'xl' {
        @media ( min-width: $bp-xl ) { @content; }
    }
}

// 使用例(モバイルファースト)
.section-title {
    font-size: $font-size-2xl;

    @include mq( 'md' ) {
        font-size: $font-size-3xl;
    }

    @include mq( 'lg' ) {
        font-size: $font-size-4xl;
    }
}

5.3 よく使うミックスイン集

// ============================================
// よく使うミックスイン集
// ============================================

// テキストの省略(1行)
@mixin text-ellipsis {
    overflow:      hidden;
    white-space:   nowrap;
    text-overflow: ellipsis;
}

// テキストの省略(複数行)
@mixin line-clamp( $lines: 3 ) {
    display:            -webkit-box;
    -webkit-line-clamp: $lines;
    -webkit-box-orient: vertical;
    overflow:           hidden;
}

// アスペクト比を固定する
@mixin aspect-ratio( $ratio: '16/9' ) {
    aspect-ratio: #{$ratio};
    overflow:     hidden;
}

// 絶対配置で中央揃え
@mixin absolute-center {
    position:  absolute;
    top:       50%;
    left:      50%;
    transform: translate( -50%, -50% );
}

// すりガラス効果
@mixin glass-effect(
    $bg:     rgba( 255, 255, 255, 0.15 ),
    $blur:   12px,
    $border: rgba( 255, 255, 255, 0.2 )
) {
    background:              $bg;
    backdrop-filter:         blur( $blur );
    -webkit-backdrop-filter: blur( $blur );
    border:                  1px solid $border;
}

// ホバー時の浮き上がり効果
@mixin hover-lift( $y: -4px, $shadow: $shadow-lg ) {
    transition: transform $transition-base,
                box-shadow $transition-base;

    &:hover {
        transform:  translateY( $y );
        box-shadow: $shadow;
    }
}

// スクリーンリーダー専用テキスト
@mixin sr-only {
    position: absolute;
    width:    1px;
    height:   1px;
    padding:  0;
    margin:   -1px;
    overflow: hidden;
    clip:     rect( 0, 0, 0, 0 );
    border:   0;
}

// 使用例
.card-title { @include text-ellipsis; }
.card-text  { @include line-clamp( 2 ); }

.hero-image {
    @include aspect-ratio( '16/9' );
    img { width: 100%; height: 100%; object-fit: cover; }
}

.glass-header { @include glass-effect; }

.portfolio-card { @include hover-lift; }

.visually-hidden { @include sr-only; }

6. 関数(Functions)

6.1 Sassの組み込み関数

// ============================================
// よく使うSassの組み込み関数
// ============================================

// 色を操作する関数
$base: #1A5FC0;

.example {
    // 暗くする
    color: darken( $base, 10% );     // → 10%暗い
    // 明るくする
    color: lighten( $base, 10% );    // → 10%明るい
    // 透明にする
    color: rgba( $base, 0.5 );       // → 50%透明
    // 彩度を下げる
    color: desaturate( $base, 20% ); // → 彩度-20%
    // 補色を取得する
    color: complement( $base );      // → 反対色
    // 色を混ぜる
    color: mix( $base, #fff, 80% );  // → 白と80:20で混合
}

// 数値を操作する関数
.calc-example {
    width:  percentage( 1/3 );          // → 33.3333%
    height: round( 16.7px );            // → 17px
    margin: ceil( 10.2px );             // → 11px(切り上げ)
    padding: floor( 10.8px );           // → 10px(切り捨て)
    font-size: max( 1.4rem, 16px );     // → 大きい方を使う
    font-size: min( 1.6rem, 18px );     // → 小さい方を使う
    width: clamp( 300px, 50%, 600px );  // → 範囲内に収める
}

// 文字列を操作する関数
$prefix: 'is-';
.#{$prefix}active { color: red; } // → .is-active { color: red; }

6.2 カスタム関数を作る

// @function でオリジナルの関数を定義する

// rem変換関数(pxをremに変換する)
@function rem( $px, $base: 16 ) {
    @return ( $px / $base ) * 1rem;
}

// 使用例
.title {
    font-size: rem( 32 );  // → 2rem
    margin:    rem( 24 );  // → 1.5rem
}

// ============================================

// コンテナの幅を計算する関数
@function container-width( $columns, $total: 12, $gutter: 24px ) {
    @return calc(
        ( 100% * #{$columns} / #{$total} ) - #{$gutter}
    );
}

// z-indexを管理する関数
$z-index-map: (
    'behind':   -1,
    'base':      0,
    'raised':    1,
    'dropdown': 10,
    'sticky':   50,
    'modal':   100,
    'toast':   200,
);

@function z( $layer ) {
    @if map-has-key( $z-index-map, $layer ) {
        @return map-get( $z-index-map, $layer );
    }
    @warn "z-index '#{$layer}' は定義されていません";
    @return 0;
}

// 使用例
.header   { z-index: z( 'sticky' ); }  // → 50
.modal    { z-index: z( 'modal' ); }   // → 100
.dropdown { z-index: z( 'dropdown' ); }// → 10

7. ファイルの分割と@use

7.1 ファイルを分割して管理する

推奨のファイル構成:

src/
  scss/
    ├── abstracts/
    │   ├── _variables.scss  # 変数
    │   ├── _mixins.scss     # ミックスイン
    │   └── _functions.scss  # 関数
    ├── base/
    │   ├── _reset.scss      # リセットCSS
    │   └── _typography.scss # タイポグラフィ
    ├── components/
    │   ├── _button.scss     # ボタン
    │   ├── _card.scss       # カード
    │   ├── _modal.scss      # モーダル
    │   └── _carousel.scss   # カルーセル
    ├── layout/
    │   ├── _header.scss     # ヘッダー
    │   ├── _footer.scss     # フッター
    │   └── _grid.scss       # グリッド
    ├── pages/
    │   ├── _home.scss       # トップページ
    │   └── _contact.scss    # お問い合わせ
    └── style.scss           # エントリーポイント

7.2 @use で読み込む(モダンな書き方)

// style.scss(エントリーポイント)

// @use でファイルを読み込む(モダンな書き方)
@use 'abstracts/variables' as v;
@use 'abstracts/mixins'   as m;
@use 'abstracts/functions' as f;

@use 'base/reset';
@use 'base/typography';

@use 'components/button';
@use 'components/card';
@use 'components/modal';

@use 'layout/header';
@use 'layout/footer';

@use 'pages/home';
// _button.scss
// @use で変数を読み込む

@use '../abstracts/variables' as v;
@use '../abstracts/mixins' as m;

.btn {
    background: v.$color-primary;
    padding:    v.$space-3 v.$space-6;
    @include m.hover-lift;

    &--secondary {
        background: v.$color-secondary;
    }
}

💡 @import は非推奨・@use を使いましょう: 古いSassの記事では @import が使われていますが現在は非推奨になっています。@use を使うとスコープが明確になり変数の衝突も防げます。


8. 実践的なSCSSファイル例

8.1 カードコンポーネントのSCSS

// _card.scss

@use '../abstracts/variables' as v;
@use '../abstracts/mixins'    as m;

.card {
    background:    v.$color-bg;
    border:        1px solid v.$color-border;
    border-radius: v.$radius-lg;
    overflow:      hidden;
    @include m.hover-lift;

    // 画像
    &__image {
        @include m.aspect-ratio( '16/9' );

        img {
            width:      100%;
            height:     100%;
            object-fit: cover;
            transition: transform v.$transition-slow;
        }
    }

    &:hover &__image img {
        transform: scale( 1.05 );
    }

    // 本文エリア
    &__body {
        padding: v.$space-6;
        display:        flex;
        flex-direction: column;
        gap:            v.$space-3;
    }

    // タグ
    &__tags {
        display:    flex;
        flex-wrap:  wrap;
        gap:        v.$space-2;
    }

    &__tag {
        font-size:        v.$font-size-xs;
        padding:          v.$space-1 v.$space-2;
        background:       rgba( v.$color-primary, 0.1 );
        color:            v.$color-primary;
        border-radius:    v.$radius-full;
        font-weight:      600;
    }

    // タイトル
    &__title {
        font-size:   v.$font-size-xl;
        font-weight: 700;
        line-height: 1.4;
        @include m.line-clamp( 2 );
    }

    // テキスト
    &__text {
        font-size:   v.$font-size-sm;
        color:       v.$color-text-sub;
        line-height: 1.7;
        @include m.line-clamp( 3 );
        flex: 1;
    }

    // フッター
    &__footer {
        padding:     v.$space-4 v.$space-6;
        border-top:  1px solid v.$color-border;
        @include m.flex-between;
    }

    // バリアント
    &--horizontal {
        display: grid;
        grid-template-columns: 200px 1fr;

        .card__image {
            aspect-ratio: auto;
            height:       100%;
        }
    }

    &--featured {
        border:     none;
        box-shadow: v.$shadow-lg;

        .card__title {
            font-size: v.$font-size-2xl;
        }
    }
}

9. 実践チェックリスト

✓ npm install -D sass でSassをインストールして .scss ファイルで書けるか確認したか

✓ 変数ファイル(_variables.scss)でカラー・スペーシング・ブレークポイントを一元管理しているか

✓ & を使ってBEM記法(.card__title・.card--featured)をネストで書けるか

✓ メディアクエリのミックスイン(@include mq('md'))を作ってモバイルファーストで書けるか

✓ メディアクエリのミックスイン(@include mq('md'))を作ってモバイルファーストで書けるか

✓ @import ではなく @use でファイルを読み込んでいるか

✓ ファイルをabstracts・base・components・layout・pagesに分割して管理しているか

✓ darken() / lighten() / rgba() などSassの色関数を活用しているか

✓ 3階層以上の深いネストを避けているか


まとめ

今回はSassの基本的な使い方として変数・ネスト・ミックスイン・関数を解説しました。ポイントをまとめると:

  • SassはCSSを拡張したメタ言語でコンパイルが必要・ViteやLive Sass Compilerで簡単に導入できる
  • 変数($color-primary)でカラー・スペーシング・ブレークポイントを一元管理すると変更コストが大幅に減る
  • ネストでHTML構造に合わせてCSSを整理できる・& で親セレクターを参照してBEM記法と相性よく使える
  • ミックスイン(@mixin / @include)でメディアクエリ・すりガラス・テキスト省略などをまとめて再利用できる
  • darken() / lighten() / rgba() など組み込み関数で色を操作できる・@function でカスタム関数も作れる
  • ファイルをabstracts・base・components・layoutに分割してコンポーネントごとに管理するとメンテナンスが楽になる
  • @import は非推奨・@use を使ってスコープを明確にする
  • ネストは3階層以下に抑える・深くしすぎると詳細度が上がり上書きしにくいCSSになる

次の記事では、Gitの基本的な使い方を解説します。バージョン管理の仕組み・コミット・ブランチ・GitHubへのプッシュなど、制作現場で必須のGitをゼロから学べる内容をお届けします。お楽しみに!

コメント

タイトルとURLをコピーしました