CSSコーディングテクニック : 詳細度、単位、flexbox、mixin

(2016/7/15、記事を修正いたしました。)

最近、ビギナーからベテランのデベロッパに至るまで、CSSに手を焼く人を多く見かけます。そうした人たちの中には、CSSの機能を好まず、別の言語を使った方がいいのではないかと考えている人もいます。もともと、CSSのプロセッサもこうした考え方から生まれました。書くべきコードを少なくできることを期待して(以前の記事でご紹介しているとおり、普通はそうではありません)CSSのフレームワークを使う人もいれば、CSSを完全に見限り、スタイルの指定にJavaScriptを使うという人もいます。

しかし、あなたが取り組んでいるパイプラインにCSSプロセッサをいつも取り入れる必要があるとは限りません。どんなプロジェクトであれ、開始の時点から、膨れ上がったフレームワークをデフォルトに取り込む必要はありません。また、CSSを使うところで、代わりにJavaScriptを使おうとすると、散々な目に遭うことになります。

本記事では、維持しやすいCSSコード、言い換えれば、ルールをできるだけ減らした短いスタイルシートをうまく書くためのヒントとオススメをご紹介したいと思います。CSSが厄介者ではなく、便利なツールと感じられるようになると思います。

「最小単位の実行可能なセレクタ」

CSSは宣言型の言語です。DOMで要素のスタイルを決めるルールを特定します。この言語では、ルールが適用される順序の中で、他のルールより優先されるルールがあります。例えばインラインスタイルといったもので、前に適用されたルールをオーバーライドします。

例として、以下のHTMLとCSSコードの場合を挙げてみます。

<button class="button-warning">
.button-warning {
  background: red;
}

button, input[type=submit] {
  background: gray;
}

この場合、最初に.button-warningのルールが定義されていますが、その後に定義されるbutton, input[type=submit]が、最後のbackgroundプロパティをオーバーライドしてしまいます。なぜでしょうか? 一方のスタイルがもう一方のスタイルをオーバーライドする場合、適用される方を決める基準は何なのでしょうか?

それは、詳細度です。

セレクタには、他よりも詳細な内容であると見なされるものがあります。例えば、#idセレクタは.classセレクタをオーバーライドします。

では、本当に必要としているものよりも詳細であると見なされるセレクタを使ったらどういうことが起こるでしょうか? 後になってそのスタイルをオーバーライドしたい場合には、もっと詳細なセレクタが必要になります。さらにその後で、この詳細なセレクタをオーバーライドしたい場合には、もっと、もっと…。と、そうです、これではスタイルシートは雪だるま式に大きくなり、最終的には維持することが非常に難しくなるのです。

ですから、セレクタを書くときは常に、「これは、ここで必要な役目を果たすセレクタとして最も低い詳細度なのか?」と自問してみることが大切です。

詳細度というルールの全てについては、正式にW3C CSSセレクタ仕様に定義されていますこれは、全てのCSSセレクタについて、1つ1つの詳細度を調べる手段です。より理解しやすいものについては、CSS詳細度に関するこちらの記事を読んでください。

バグで新しいルールを捨てないこと

こんな典型的なシチュエーションを想像してみましょう。あなたのCSSにバグがあったため、どのDOM要素が間違ったスタイルを持っているのかを探しています。すると、どういうわけか、間違ったプロパティが継承されていることに気付くのです。

そこで、単にCSSを書き足さないでください。そうすると、あなたのコードベースは少し大きくなり、その先起こり得るバグを見つけるのが少々難しくなります。

代わりに、いったん立ち止まり、距離を置いてみましょう。そして、要素を詳しく調べるためにブラウザでデベロッパツールを使い、カスケード全体を見てみます。あなたが意図していないスタイルを指定しているのはどのルールなのか、正確に識別します。そして、そうした間違った結果にならないように、その既存のルールを変更するのです。

Firefoxでは、ページ内の要素を選んで右クリックし、Inspect element(訳注:日本語版では「要素を調査」)を選択すると、CSSファイルをデバッグすることができます。

image00

称賛に値するこのカスケードを見てください。ここでは、各要素に適用されるルールの全てを、適用される順序のとおりに確認することができます。トップに入力された要素はより高い詳細度を有するものであり、それより前のスタイルをオーバーライドすることができます。中には、いくつかのプロパティを持つルールに削除線が引かれていることも分かります。つまり、より詳細とされるルールがそのプロパティをオーバーライドしているのです。

また、あなたはルールを見るだけでなく、実際にオンとオフを切り替えたり、その場で臨機応変に変更したりすることにより、結果を観察することができます。これは、バグの修正をする際には大変便利です。

必要な修正はルールの変更かもしれませんし、カスケードの中の異なるポイントでのルール変更かもしれません。修正には新しいルールが必要になる場合もあります。ただ、少なくとも、それが正しい判断だったということと、あなたのコードベースが何を必要としているかということは分かるでしょう。

これは、リファクタリングの機会を探すのにいいタイミングでもあります。CSSはプログラミング言語ではありませんが、ソースコードです。JavaScriptやPythonに対して配慮するのと同じように、CSSにも気を配る必要があります。つまり、きれいに、読めるように、そして必要なときにリファクタリングができるようにしておくべきなのです。

!important は使わないこと

このことは以前の勧告からも暗に伝えられてはいますが、重要ですから再度強調しておきます。コードの中で、!importantを使ってはいけません。

!importantとは、カスケードを無視することができるCSSの機能です。CSSとは、「Cascading Style Sheets(訳注:カスケードとは日本語で”階段状に連続した滝”を意味します)」の略で、これがヒントです。

!importantは、急いでバグを修正しようとしているときに、カスケードを修正する時間や意志がない場合に使われがちです。また、非常に詳細なルールを持ったCSSフレームワークを含むために、単純に大変すぎてオーバーライドできないという場合にも多く使われる傾向があります。

!importantをプロパティに追加すると、ブラウザはその他の高い詳細度のルールを無視します。!importantのルールを使って、同じく!importantとマークされた別のルールをオーバーライドしようとした場合には、本当に困った状況に陥るということはお分かりいただけるはずです。

ただし1つだけ、!importantにも合理的な使い方があります。デバッグのためにデベロッパツールを使って作業をする少しの時間だけ使うのです。あるバグを修正するにはどのプロパティ値を変えなければいけないのかを探すことがあります。そんなとき、デベロッパツールに!importantを使い、CSSルールをその場で変更することで、カスケードを無視して値を見つけることができます。

CSSのどの部分がバグを起こしているのかが分かったらコードに戻り、カスケードのどのポイントにそうしたCSSを入れ込みたいのかを見ることができます。

px と % 以上に重要なものがある

px(ピクセル)および%(パーセント)単位での作業は非常に直感的なものなので、ここではあまり知られていない、あるいはあまり直感的でない単位に注目してみましょう。

em と rem

最もよく知られている相対的な単位はemです。1emは、その要素のフォントサイズに相当します。

次のような小さなHTMLがあるとします。

<article>
  <h1>Title</h1>
  <p>One Ring to bring them all and in the darkness bind the.</p>
</article>

加えて、これだけのルールからなるスタイルシートがあるとします。

article {
  font-size: 1.25em;
}

ほとんどのブラウザは、デフォルトではルート要素(ところで、これはユーザによってオーバーライドできます。素晴らしいアクセシビリティ機能です)に16ピクセルのベースフォントサイズを適用しています。というわけで、このarticle要素のテキストはおそらく20ピクセル(16 * 1.25)のfont-sizeでレンダリングされます。

h1はどうでしょうか? どうなるかをもっとよく理解するために、スタイルシートに、この他のCSSルールを追加してみましょう。

h1 {
  font-size: 1.25em;
}

これもまた1.25emであり、先のarticleの場合と同じではありますが、しかし、こちらはem単位が組み合わされていることを考慮する必要があります。というのは、例えばh1がbody要素の直接の子要素だった場合、そのfont-sizeは20ピクセル(16 * 1.25)になります。しかし、ここでのh1はルートとは異なるfont-sizeを持つ要素(article)の内部にあるのです。この場合、1.25はカスケードにより与えられるfont-sizeを表します。そのため、h1は25ピクセル(16 * 1.25 * 1.25)のfont-sizeでレンダリングされるのです。

ところで、頭の中でこうした掛け算の全てを行う代わりに、Inspector(訳注:インスペクタ)タブのComputed(訳注:計算済み)を使うことができます。これは、実際の最終的な値をピクセルで表示してくれます。

image01

em単位は非常に汎用性があります。例えば、動的なものであっても、ページの全てのサイズ(font-sizeだけでなく、line-heightといった他のプロパティや、widthまで)を実に簡単に変更してくれます。

emは「ベースに対する相対サイズである」という点は好ましいけれども、組み合わせの部分が気に入らないという場合は、rem単位を使うことができます。rem単位は、組み合わせを無視して、単にルート要素のサイズを使うemのようなものです。

そのため、先のCSSを用いてh1em単位をrem単位に変更する場合は以下のとおりになります。

article { font-size: 1.25em; }
h1 { font-size: 1.25rem; }

全てのh1要素は20ピクセル(16pxのベースサイズを想定)と算出されたfont-sizeとなり、articleの中に含まれているかどうかは関係ないということになります。

vw と vh

vwvhは、ビューポート単位です。1vhは、ビューポートの高さの1%であり、1vwは、ビューポートの幅の1%です。

常に実際のbodyの大きさに関係せず、画面全体を占有する必要がある(モーダルの典型的な半透明の背景など)UI要素が必要な場合、これらは非常に便利です。

その他の単位

それほど一般的ではなく、あるいは汎用性もないと思われる単位が他にもありますが、そのようなものでは必ずつまずくでしょう。MDNで詳細を学ぶことができますよ。

flexboxを使用すること

このことについては既に、以前ご紹介しているCSSフレームワークに関する記事の中で触れています。flexboxモジュールは、レイアウトの作成や調整といった作業を単純化してくれるものです。flexboxを使ったことがないならば、こちらの入門ガイドをチェックしてみてください。

そして、そうです、今日ではflexboxが使えるのです。ただし、古いブラウザをビジネス上の理由でサポートしなければならないという場合は除きます。現在、ブラウザのflexboxに対するサポートは94%を超えています。そのため、デバッグや維持が困難なフローティングのdiv全てを書かなくてもよいのです。

なお、実装のレイアウトをスムーズに進ませる今後のグリッドモジュールについては注意を怠らないようにしてください。

CSSプロセッサを使用している場合

SassやLessなどのCSSコンパイラは、フロントエンド開発の世界で非常に人気があります。これらは強力なツールであり、うまく使えばCSSでより効率的に作業することができます。

セレクタのネストを乱用しないこと

これらのプロセッサ、または「コンパイラ」の共通の特徴は、セレクタのネストです。例えば、以下のLessのコードがあるとします。

a {
  text-decoration: none;
  color: blue;

  &.important {
    font-weight: bold;
  }
}

これは以下のCSSルールに変換されます。

a {
  text-decoration: none;
  color: blue;
}

a.important {
  font-weight: bold;
}

この機能のおかげで、書くコードを減らし、通常DOMツリーの中で一緒になっている要素に影響するルールをグループ化することができます。これはデバッグに便利です。

しかしながら、この機能が誤用され、CSSセレクタにDOM全体を複製してしまうこともよくあります。つまり、

<article class="post">
  <header>
    <!-- … -->
    <p>Tags: <a href="..." class="tag">irrelevant</a></p>
  </header>
  <!-- … -->
</article>

上のようなHTMLに対し、以下のようなCSSスタイルシートになってしまうような場合です。

article.post {
  // ... other styling here
  header {
    // ...
    p {
      // ...
      a.tag {
        background: #ff0;
      }
    }
  }
}

主な欠点と言えば、これらのCSSルールは非常に詳細なセレクタを有することです。これが避けるべきことであるということは、ここまでにお話ししてきました。過剰なネストには他の欠点もあり、それは別の記事でお話ししています。

まとめますと、ネストに自身で書かないであろうCSSルールを作らせないことです

include と extend

CSSプロセッサの役立つ機能には、mixinというものもあります。これは、CSSの再利用可能なチャンクです。例えば、ボタンのスタイルを設定したいとしましょう。それらのほとんどは、いくつかの基本的なCSSプロパティを持っています。Lessではこの例のようなmixinを作成することができます。

.button-base() {
  padding: 1em;
  border: 0;
}

そして、このようなルールを作成します。

.button-primary {
  .button-base();
  background: blue;
}

これは、以下のCSSを生成します。

.button-primary {
  padding: 1em;
  border: 0;
  background: blue;
}

ご覧のとおり、共通のコードをリファクタリングするのに非常に便利です。

Mixinを「includeする」こと以外に、これを「extendする」、あるいは「継承する」(正確な用語はツールごとに異なります)というオプションもあります。この機能は、同じルールに複数のセレクタを組み合わせることができます。

それでは、先の.button-baseのmixinを使用した例を見てみましょう。

.button-primary {
  &:extend(.button-base)
  background: blue;
}

.button-danger {
  &:extend(.button-base)
  background: red;
}

これが以下のように変換されます。

.button-primary, .button-danger {
  padding: 1em;
  border: 0;
}

.button-primary { background: blue; }
.button-danger { background: red; }

インターネット上の記事の中には、「include」のみを使うように書かれているものもあれば、「extend」のみを使うように書かれているものもあります。実際のところ、それらは異なるCSSを生成するものであり、いずれも本質的に間違ってはいません。実際のシナリオに応じて、どちらか一方を使用するといいでしょう。

では、その選択方法は? ここでも、「これを手作業で書くかどうか?」と考える経験則に基づくことになります。

本記事の内容があなたのCSSコードに反映され、より良いルールを記述できる助けになればと願っています。最初にお話ししたことを思い出してください。CSSもコードであり、それ自体が、あなたのコードベースの他の部分と同様に、注目され、気を配られる価値があります。そこもよくかまってあげると、報われますよ。