Sassの@at-rootをBEM記法で上手く使う方法 ~ Blockより親要素のクラスでElementとの子孫セレクタを作りたい

  • 更新日:
  • 公開日:

SassでBEM記法を使っていると、綺麗にコーディングできない場面が出てきていました。

どんな場面か。それはBlockよりも親要素のクラスでElementとの子孫セレクタを作りたいときです。これにはSassの@at-rootを使うのですが、ネットで調べても中々思い通りの手法にたどり着けず、なんとか自分で使い方を理解し解決できた次第です。(コードにするとなんてことない内容なのですが…)

例えばどんな実装か

実装として、メインビジュアルの中身を以下2種類のどちらかに切り替える機能があるとします。

  1. 画像(<img>)
  2. 動画(<iframe>など)

これらをレスポンシブ対応する場合、imgiframeのCSS内容を変える必要があるとします。

JavaScirptで「img or iframe」を判別して動的にModifierクラスを付与する解決方法もありますが、今回はサーバーサイドでbodyタグに以下クラスが予め付与されている前提とします。

  • 画像ならhas-image
  • 動画ならhas-video

予めbodyに付与されるクラス名を元に、BlockやElementのCSSプロパティを複合セレクタで変更します。

コードやクラス付与状況の例

実際にコードの実装例です。以下のHTML(PHP)がメインビジュアル用のコードです。

<body class="has-video">
    <div class="main-visual">
        <div class="main-visual__inner">
            <?php メインビジュアル表示関数など(); ?>
        </div>
    </div>
</body>

「main-visual」Blockの外、bodyに「has-video」というクラスが付与されています。これはCMS側で「メインビジュアルには画像ではなく、動画を表示する」という設定がされている状況です。画像ならばbodyタグには「has-image」です。

それでは、この親要素(bodyタグ)のクラスを元にメインビジュアルのCSSを切り替える@at-rootを使用したコードを示します。

.main-visual {

    &__inner {
        width: 100%;
        padding: 0;
        text-align: center;
    }

    @at-root .has-video & {

        &__inner {
            position: relative;
            padding-top: 56.25%;

            iframe {
                position: absolute;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
            }
        }
    }
}

肝は9行目の「@at-root」と「&」です。@at-rootは入れ子状態のセレクタを外に出す機能で、&(アンパサンド)は直前の親セレクタ名(今回で言えば”.main-visual”)を返す機能になっています。2つでクラス名を挟むと以下のCSSで出力されます。

.main-visual__inner {
    width: 100%;
    padding: 0;
    text-align: center;
}

.has-video .main-visual__inner {
    position: relative;
    padding-top: 56.25%;
}

.has-video .main-visual__inner iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

このような形で、Blockより親要素のクラスを元に、Elementとの子孫セレクタを作ることができました。

(このコンパイルが通ったSassのバージョンは「3.4.23」です)


ちなみに今までの自分はこんな風に記述していました。コーディングしていたときは歯痒かったですね…。

.main-visual {

    &__inner {
        width: 100%;
        padding: 0;
        text-align: center;
    }
}

.has-video {
    
    .main-visual__inner {
        width: 100%;
        position: relative;
        padding-top: 56.25%;

        iframe {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
        }
    }
}

そのほか「_has-video.scss」ファイル内に記述したりなど。わだかまりが一つなくなり良かったです。

書いた人

Symbol Mark

Ryoichi(しつ)

除菌ティッシュを買い込んで使いきれずによく乾かす人。

療養目的で退職し、どうやって生きていくか模索中。最近は勉強目的でLaravelやVue.js弄ったり、趣味で音で遊んでます。

※2019年10月16日現在ブログリニューアル中です。崩れなどが発生していたらすみません。

うぇぶ: @s_ryone