いいとこすぎて移住しちゃいました / LAMP壱岐
いいとこすぎて移住しちゃいました / LAMP壱岐
2015.07.24

【CSS設計】拡張や修正に強いコンポーネントをマークアップする6つのテクニック

せいと

こんにちは! フロントエンドエンジニアのせいとです。
皆様、CSS書いてますでしょうか。

今回はSMACSSやBEMをある程度ご存知の方向けに、コンポーネントをマークアップする際に役立つテクニックと題して、「SMACSSを使う際に役立つテクニック」「BEMを使う際に役立つテクニック」「さまざまなシーンで役立つテクニック」をご紹介したいと思います。
なお、SMACSSやBEMをご存知ない方は、本記事の参考文献&記事のリンクが参考になると思いますので、はじめにこちらをご参照いただければと思います。

※「コンポーネント」という言葉は、使われるシーンによってさまざまな意味になりえますが、本記事においては「Webページを構成するパーツ」と定義させていただきます。

SMACSSを使う際に役立つテクニック編

まずはSMACSSを使う際に役立つテクニックからご紹介します。

Descendent Name

Descendent Nameは親のクラス名を受け継いでいくクラスの命名アイデアです。
実際に例を上げてみましょう。

<div class=“media“>
	<img class=“media-image“ src=“...“ alt=“...“>
	<div class=“media-body“>
		<h2 class=“media-heading“>...</h2>
		<div class=“media-text“>...</div>
	</div>
</div>

上の例は「.media」というコンポーネントをマークアップした例です。
「.media」内の要素のクラス名が、全て親要素のクラス名「.media」を受け継いでいるのがわかるかと思います。

この命名ルールはBEMに少し似ています。
BEMはクラス名のバッティング回避、コンポーネントの再利用、コンポーネントのHTML構造の明瞭化などの点において優れています。
しかしその反面、ルールが厳格であったり、SMACSSなど他のアプローチと併用して使うのが難しかったりなど、導入の障壁が比較的高いです。

もしこのアプローチを使うのであれば、(BEMほど厳格で明瞭な書き方はできないものの)BEMの利点を生かしつつ比較的楽に導入にすることができます。

レイアウト調整用CSSの活用

コンポーネントをWebページに配置する際、レイアウト調整として汎用的に使えるCSSとクラスを用意しておくと、楽にWebページをつくることができます。
それは例えば以下のようなものです。

<ul class=“l-grid l-grid-col3“>
	<li class=“l-grid-item“>
		<img class=“thumbnail“ src=“...“ alt=“...“>
	</li>
	<li class=“l-grid-item“>
		<img class=“thumbnail“ src=“...“ alt=“...“>
	</li>
	<li class=“l-grid-item“>
		<img class=“thumbnail“ src=“...“ alt=“...“>
	</li>
</ul>
.l-grid {
    font-size: 0; /* inline-blockによる隙間を埋める */
}
.l-grid-item {
	display: inline-block;
	vertical-align: top;
	font-size: 10px;
}

.l-grid-col2 > .l-list-item {
	width: 50%;
}
.l-grid-col3 > .l-list-item {
	width: 33.3333%;
}
.l-grid-col4 > .l-list-item {
	width: 25%;
}

このように、レイアウト調整用CSSを使えば同じようなCSSを何度も書くことなく、再利用してコンポーネントを配置することができます。
レイアウト調整用CSSは自作してもいいですし、以下のようなCSSライブラリを使うことを検討してもよいでしょう。

BEMを使う際に役立つテクニック編

続いて、BEMを使う際に役立つテクニックです。

Mixes

Mixesは複雑なコンポーネントをマークアップする際に役立つテクニックです。
BEMの公式で紹介されている方法で、もしBEMやそれに準ずるマークアップを行っているのなら有効な手段でしょう。

ではどのようなシーンで利用できるのか。

<button class=“button“>Button</button>
.button {
	display: inline-block;
	color: #FFF;
	background: #000;
	text-decoration: none;
	padding: 5px 20px;
	border-radius: 3px;
	font-size: 15px;
}

例えばよくあるシンプルなボタンがあったとします。
 

<!-- Before -->
<form class=“form“>
	<table class=“form__table“>...</table>
	<button class=“button“>利用規約に同意して送信する</button>
</form>

このボタンを、お問い合わせのフォーム内の一番下に「利用規約に同意して送信する」というテキストを内包した形で配置したいとします。
 

<!-- After -->
<form class=“form“>
	<table class=“form__table“>...</table>
	<button class=“form__button-small button“>利用規約に同意して送信する</button>
</form>
.form__button-small.button {
	font-size: 13px;
}

しかしテキストが長すぎて見栄えが悪いため、このときのボタンのみフォントサイズを13pxに落としたい……という場合、Mixesによるマークアップが有効です。
 

.form .button {font-size: 13px;}

この他のスタイリングだと、上記のような書き方も考えられます。

ただしこの方法だと、もし「.form」内に複数の「.button」を配置することになった場合、意図しない「.button」にまで影響してしまいます。
また、Modifierクラスで対応することも検討できますが、あまり再利用する機会のない特異パターンの場合は不容易にModifierクラスを用意せず、Mixesで対応したほうが好ましいでしょう。

Wrap

Wrapもまた、複雑なコンポーネントをマークアップする際に役立つテクニックで、BEMとの相性がいいです。
いくつかの記事で紹介されていましたが、正式な呼び名がないため僕はWrapと呼んでいます。

ではWrapはどのようなシーンで利用できるのか。
例えば先ほどのボタンコンポーネントを、今度はヘッダーの中に右寄せで配置したいというとき、Wrapによるマークアップが有効です。

<header class=“site-header“>
	<div class=“site-header__button-wrap“>
		<button class=“button“>Button</button>
	</div>
</header>
.site-header__button-wrap {
	float: right;
}

この方法であれば、BEMのHTML構造を維持したままボタンを配置することができます。

Mixesを使ってもいいですが、Mixesの場合は「.button」コンポーネント自体にスタイルが追加されます。
あてるスタイルによってはコンポーネントの見た目に影響をおよぼしてしまうため、Wrapは今回の例のようにコンポーネント自体には影響を与えず、配置だけ操作したいといった場合に有効です。

margin調整用のModifierクラス

CSS設計を意識したコーディングを行っている場合、Modifierクラス(あるいはSkin、またはサブモジュールクラス)を利用する機会があるでしょう。
これらのクラスは主にコンポーネントの見た目を変え、バリエーション違いを作るために用いられます。

<button class=“button“>Button</button>
<button class=“button button--primary“>Button</button>
<button class=“button button--error“>Button</button>
.button {
	display: inline-block;
	text-decoration: none;
	padding: 5px 20px;
	border-radius: 3px;
	font-size: 15px;
	background: #000;
	color: #fff;
}
.button--primary {
	background: #27B018;
}
.button--error {
	background: #F66A4E;
}

これ以外に、Modifierクラスはコンポーネント間のmarginを操作する目的で利用することができます。

例えば以下のようなHTMLがあった場合、セクションとセクションの間にはmarginが設けられていることでしょう。
そうした場合、Modifierクラスでmarginを設定する方法を使うと、こうなります。

<section class=“section section--layout-a“>
	<h1>Section title</h1>
	<p>...</p>
</section>

<section class=“section section--layout-b“>
	<h1>Section title</h1>
	<p>...</p>
</section>

<section class=“section section--layout-c“>
	<h1>Section title</h1>
	<p>...</p>
</section>
.section--layout-a {
	margin-bottom: 32px;
}
.section--layout-b {
	margin-bottom: 48px;
}
.section--layout-c {
	margin-bottom: 64px;
}

marginの値がシンプルな値(10px,20pxなど)であれば、ヘルパークラス(あるいはユーティリティと呼ばれるCSS)を用いることが検討できますが、デザイン上そうもいかないことがあるでしょう。

また、複数のシーンで同じコンポーネントが再利用される場合はどこに置くかによってmarginの値が変わることも考えられますから、コンポーネント自体にmarginを設定することは避けたほうがいいといえます。

このような条件下(marginの値が細かく、かつコンポーネントを再利用することが前提の場合)では、Modifierによる指定が活用できます。

  • 1
  • 2