新人コーダーに伝えたい、きれいなCSSを書くための4つの習慣

2017/04/06

Tiffany Brown

0

Articles in this issue reproduced from SitePoint
Copyright © 2017, All rights reserved. SitePoint Pty Ltd. www.sitepoint.com. Translation copyright © 2017, KADOKAWA CorporationJapanese syndication rights arranged with SitePoint Pty Ltd, Collingwood, Victoria,Australia through Tuttle-Mori Agency, Inc., Tokyo

この春、CSSコーディングの仕事を始めた新人さんへ、「きれいなCSS」を書くためのガイドラインをお届け。

きれいなCSSを書くためにはいくつかルールがあります。ルールに従うとレイアウトの崩れを最大限防げるだけでなく、軽量で再利用可能なCSSを書けるメリットもあります。ここで紹介するルールは次のとおりです。

  • グローバルセレクターや要素セレクターを避ける
  • 詳細度が高すぎるセレクターは使わない
  • セマンティックなクラス名を使う
  • マークアップ構造とCSSを密結合しすぎない

ルールを1つずつ説明していきます。

グローバルセレクターを避ける

グローバルセレクターには全称セレクター(*)、pbuttonh1といった要素セレクター、[type=checkbox]といった属性セレクターが含まれます。これらのセレクターに適用されるスタイル宣言は、サイト全体にわたって該当する要素すべてに適用されます。以下に例を示します。

button {
  background: #FFC107;
  border: 1px outset #FF9800;
  display: block;
  font: bold 16px / 1.5 sans-serif;
  margin: 1rem auto;
  width: 50%;
  padding: .5rem;
}

上のコードはなにも問題ないように思えます。しかし、スタイルの違うボタンを作成したい場合はどうなるでしょうか。ダイアログを閉じるモジュールで使われる.closeボタンをスタイリングするとします。

<section class="dialog"> 
  <button type="button" class="close">Close</button> </section>

■要素としてdialogが使われていない理由
ここで要素としてdialogではなくsectionが使われているのはdialog対応ブラウザーがChrome/Chromium、Opera、YandexといったBlinkベースのものに限られているからです。

さて、CSSを書くときにbuttonのルールセットを継承したくない行は、すべて次のようにオーバーライドしなければなりません。

.close {
  background: #e00;
  border: 2px solid #fff;
  color: #fff;
  display: inline-block;
  margin: 0;
  font-size: 12px;
  font-weight: normal;
  line-height: 1;
  padding: 5px;
  border-radius: 100px;
  width: auto;        
}

ブラウザーのデフォルト値をオーバーライドするために、このような宣言がほかにもたくさん必要になるでしょう。むしろbuttonのスタイルを.defaultクラスの範疇として設定したらどうでしょうか。そうすれば.closeのルール設定からdisplayfont-weightline-heightmarginpaddingwidth宣言を省けます。省くことでファイルサイズを23%削減できます。

.default {
  background: #FFC107;
  border: 1px outset #FF9800;
  display: block;
  font: bold 16px / 1.5 sans-serif;
  margin: 1rem auto;
  width: 50%;
  padding: .5rem;
}

.close {
  background: #e00;
  border: 2px solid #fff;
  color: #fff;
  font-size: 12px;
  padding: 5px;
  border-radius: 100px;
}

同様に大切なこととして、グローバルセレクターを避けるとスタイルが衝突するリスクを減らせます。あるモジュールやドキュメントを手がけている開発者が、別のモジュールやドキュメントで副作用を引き起こすようなルールをうっかり追加することもなくなるでしょう。

グローバルスタイルやグローバルセレクターは、デフォルトでのブラウザーのスタイルのリセットや正規化にかけては完璧です。しかし、コードの肥大化を招きます。

詳細度が高すぎるセレクターは使わない

セレクターの詳細度を低くしておくことは、軽量で再利用・メンテナンス可能なCSSを作成する秘訣の1つです。知ってのとおりタイプセレクターの詳細度は「0,0,1」ですが、クラスセレクターは「0,1,0」です。

/* Specificity of 0,0,1 */
p {
  color: #222;
  font-size: 12px;
}

/* Specificity of 0,1,0 */
.error {
  color: #a00;
}

要素にクラス名を追加すると、そのセレクター(クラスセレクター)のルールはもっと包括的なタイプセレクターのルールよりも優先されます。念のためにと考えて、クラスセレクターをタイプセレクターと組み合わせる必要はありません。セレクターの詳細度は上がりますが、ファイルサイズ全体も大きくなります。

言い換えると.errorでも同じ目的を達成できるのでp.errorを使って詳細度を上げる必要はないということです。.errorを使うとほかの要素で再利用できるというメリットもあります。セレクターをp.errorとすると.errorクラスがその要素に限定されてしまいます。

クラスをチェーンしない

クラスセレクターのチェーンも避けてください。.message.warningといったセレクターの詳細度は「0,2,0」です。詳細度が高くなるとオーバーライドしにくくなるだけでなく、チェーンはしばしば副作用の原因にもなります。次の例を見てください。

message {
  background: #eee;
  border: 2px solid #333;
  border-radius: 1em;
  padding: 1em;
}
.message.error {
  background: #f30;
  color: #fff;
}
.error {
  background: #ff0;
  border-color: #fc0;
}

<p class="message">にこのCSSを適用すると、図2.1のようにダークグレーのボーダーで囲まれたグレーのボックスがうまく表示されます。

図2.1:.messageセレクターでの表示結果

ところがマークアップが<p class="message error">の場合、図2.2に示されるように背景には.message.errorのスタイル、ボーダーには.errorのスタイルが適用されます。

図2.2:セレクターとして.message.errorを使った場合の表示結果

チェーンされたクラスセレクターをオーバーライドする唯一の方法は、より詳細度の高いセレクターを使うことです。ボーダーが黄色で表示されないようにするにはクラス名を追加するか、または.message.warning.exceptiondiv.message.warningのようにタイプセレクターをチェーンしなければなりません。新しくクラスを作成するほうが効果的です。セレクターをチェーンしているなら設計を練り直します。デザインに矛盾があったり、ありもしない問題を防ごうと早まってチェーンを使っているかもしれません。こうした問題は解消してください。メンテナンスで頭痛の種を抱えずに済むだけでなく再利用性も得られ、メリットがあります。

idセレクターの使用を避ける

1つのドキュメント内でidは1つの要素に対してしか使えないため、idセレクターを使ったルールセットは再利用が困難です。一般にidセレクターを使う場合、たとえば#sidebar-features#sidebar-sportsといったidセレクターのリストを使う必要があります。

id属性も詳細度が高いので、宣言をオーバーライドするにはセレクターを長くしなければなりません。次のCSSで#sidebarの背景色をオーバーライドするには#sidebar.sports#sidebar.localを使う必要があります。

#sidebar {
  float: right;
  width: 25%;
  background: #eee;
}
#sidebar.sports  {
  background: #d5e3ff;
}
#sidebar.local {
  background: #ffcccc;
}

.sidebarといったクラスセレクターに切り替えれば、セレクターのチェーンを省略して次のようにできます。

sidebar {
  float: right;
  width: 25%;
  background: #eee;
}
.sports  {
  background: #d5e3ff;
}
.local {
  background: #ffcccc;
}

数バイトの節約になるだけでなく.sports.localのルールセットをほかの要素に追加できるようになりました。

[id=sidebar]といった属性セレクターを使うと、id属性で詳細度が高くなることを避けられます。クラスセレクターのような再利用性はありませんが、詳細度が低くなるのでセレクターのチェーンを避けられます。

■idセレクターで詳細度を高くすることがメリットとなる場合
状況によってはidセレクターで詳細度を高くすることが望ましい場合もあります。たとえばメディアサイトのネットワークでは、一貫して同じナビゲーションバーを使うのが望ましいでしょう。こうしたコンポーネントはネットワーク内のサイト全体で統一されるべきであり、簡単に再スタイリングされないようにしなければなりません。idセレクターを使えば、誤ってスタイルがオーバーライドされるリスクを減らせます。

最後に#main article.sports table#stats tr:nth-child(even) td:last-childといったセレクターについて取り上げます。こうしたセレクターは長すぎるだけでなく、詳細度が「2,3,4」となって再利用もできません。このようなセレクターをマークアップしても大丈夫と言えるケースはどれほどあるでしょうか。

改善しましょう。とりあえずこのセレクターの一部をカットして#stats tr:nth-child(even) td:last-childに変更できます。詳細度としてはこれで十分通用しますが、クラス名を使うほうがはるかに良い手法です。再利用性も得られ、バイト数も最小化できます。

■プリプロセッサーでのネストに起因する現象
プリプロセッサーでネストを使い過ぎてしまい、セレクターの詳細度が高くなりすぎている場合がよくあります。

セマンティックなクラス名を使う

「セマンティックな」という用語は「意味をなす」ことを表します。クラス名は、適用されるルールや影響するコンテンツタイプを記述的に示すものにするのが良いでしょう。さらに、デザイン要件が変更されても使い続けられるようなクラス名が望まれます。ネーミングを考える作業は思った以上に大切なことです。

.red-text.blue-button.border-4px.margin10pxなどは良くない例です。どこが問題なのでしょうか。こうした名前は既存のデザインとのつながりが強すぎます。たとえばエラーメッセージにclass="red-text"とマークアップしてもとりあえずは大丈夫です。とはいえ、デザインを変更してエラーメッセージをオレンジのボックスに黒のテキストで表示することにした場合どうなるでしょうか。クラス名が実情に合わなくなって、開発者にとっても仕事仲間にとってもコードの表示結果が分かりにくくなるでしょう。

この場合、.alert.error.message-errorといったクラス名を使ったほうが良いでしょう。こうしたクラス名はクラスの用途や適用されるコンテンツの種類(エラーメッセージ)を示しています。ページレイアウトを定義するクラス名にはlayout-grid-col-またはシンプルにl-といったプレフィックスを付け、ひと目でその用途が分かるようにします。

CSSとマークアップを密結合させない

おそらくコード内で子セレクターや子孫セレクターを使った経験があることでしょう。子セレクターはE > Fといったパターンで記述され、要素Fに対してEが直接の親セレクターであることを表します。たとえばarticle > h1<article><h1>Advanced CSS</h1></article>h1要素に影響しますが、<article><section><h1>Advanced CSS</h1></section></article>h1要素には影響しません。一方子孫セレクターはE Fというパターンで記述され、要素Fに対してEが「先祖」であることを表します。先ほどのマークアップ例を使うと、article h1と記述した場合どちらの場合でもh1要素が選択されます。

子セレクターも子孫セレクターも本来、悪いものではありません。実際、子セレクターや子孫セレクターを使えばCSSルールの適用範囲をうまく限定できます。とはいえ、マークアップは時折変更されるので決して理想的ではありません。

次のような経験をしたことがあるかもしれません。クライアント用にテンプレートをいくつか作成し、CSSで数カ所に子セレクターと子孫セレクターを使いました。子セレクターと子孫セレクターの大半は要素セレクターでもあるので、コードのあちこちに.promo > h2.media h3といったセレクターが書かれています。クライアントが雇ったSEOコンサルタントが、マークアップをレビューしてh2要素とh3要素をh1要素とh2要素に変更するよう提案した場合、問題になるのはCSSも変更しなければならないということです。

再びクラスセレクターがメリットを発揮します。.promo > .headline.media .title、またはよりシンプルにpromo-headline.media-titleを使っていればマークアップが変更されてもCSSを変更せずに済みます。

もちろんこのルールは、マークアップ全体を利用・コントロールできることを前提としています。レガシーCMSで作業している人にとっては勝手が違う場合もあるでしょう。そうしたケースでは子セレクター、子孫セレクター、疑似クラスセレクターの使用が適切かつ必要です。

構造的に優れたCSSルールについてさらに詳しく

Philip Waltonの記事『CSS Architecture』ではこのようなルールをはじめ別のルールについても取り上げています。CSSアーキテクチャに関するさらに詳しい考察として、Harry Robertのサイトの『CSS Guidelines』、Nicolas Gallagherの投稿『About HTML Semantics and Front-end Architecture(HTMLのセマンティクスとフロントエンドアーキテクチャ)』もおすすめです。

※この記事はTiffany Brownの著書『CSS Master』から抜粋したものです。この本は世界中の書店で販売されていますが、ebook版はこちらから購入できます。

(原文:Golden Guidelines for Writing Clean CSS

[翻訳:新岡祐佳子/編集:Livit

Copyright © 2017, Tiffany Brown All Rights Reserved.

Tiffany Brown

Tiffany Brown

ロサンゼルスに拠点を置くフリーランスのWeb開発者・テクニカルライターです。大小のクライアントにWeb開発・コンサルティングサービスをしています。Opera Softwareのdeveloper relationsチームの元メンバーで、SitePointの書籍『JumpStart HTML5』の共同執筆者でもあります。ブログでWeb開発技術についてときどき執筆しています。Twitterのフォローは@webinistaです。

Loading...