本付録は、メインシリーズであるWordPressのテーマ作成に役立ちそうな情報を補足していくパートだ。
前回は、CSS設計において共通している4つのゴールと、8つの考え方について解説した。
…と書いたが、考え方の最後一つは紹介だけで、本記事でしっかり扱っていく。
これらはCSS設計の根底にある考え方で、常に意識しておきたいポイントになる。
具体的な手法を紹介する際にも、8つの考え方のうちどれを参考にその規則があるかを明記していくので、それと照らし合わせながら理解を深めていってほしい。
以下がその記事だ。
さて、復習の中でも書いた通り、今回は残った一つである拡張しやすいという考え方を紹介する。
…のだが、その前に二つほど補足しておきたい内容がある。
シングルクラス設計と、マルチクラス設計という考え方だ。
これも別途具体例を用意して解説していこう。
なお、本付録は以下の本を参考にしている。
気になる方は、実際に本屋で中身を見てみたり、買ってみたりしよう。
シングルクラスとマルチクラス
さて、CSSを組んでいると以下のような状況に出くわすことがあると思う。
- あるモジュールについて、そこから少しだけスタイルが異なる部品を作りたい
- 状態の変化を実現したい
つまり、元となるモジュールがあり、それに対して少しだけ変更を加えるような状況だ。
これを実現するために、方針としては大きくシングルクラス設計と、マルチクラス設計に分けられる。
その内容に入る前に、新しくサンプルを用意しよう。
二つのボタンを用意した。
左側が元々あるモジュール、右側が少し変更を加えたモジュールだとする。
これを、それぞれシングルクラス設計、マルチクラス設計で作ってみよう。
なお、前回と同様にテキストは異なるが構造やCSS設定は参考書を引用し、またリセットCSSも同じくcss-wipeを使用している。
シングルクラス設計
もう名前から分かると思うが、シングルクラス設計では各HTMLタグにクラス名をそれぞれ一つずつのみ設定する。
こちらでサンプルを組んだ場合のHTML、CSSソースは以下の通り。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>第4回CSS設計サンプルページ</title>
<!-- css-wipeのファイル読み込み -->
<link rel="stylesheet" type="text/css" href="./css/css-wipe.css">
<!-- 自作CSSファイル読み込み -->
<link rel="stylesheet" type="text/css" href="./css/style.css">
</head>
<body>
<a class="el_btnTheme" href="#">ベースモジュール</a>
<a class="el_btnWarning" href="#">変更モジュール</a>
</body>
</html>
@charset "UTF-8";
.el_btnTheme {
display: inline-block;
width: 300px;
max-width: 100%;
padding: 20px 10px;
background-color: #e25c00;
box-shadow: 0 3px 6px rgba(0, 0, 0, .16);
color: #ffffff;
font-size: 18px;
line-height: 1.5;
text-align: center;
text-decoration: none;
transition: .25s;
}
.el_btnWarning {
display: inline-block;
width: 300px;
max-width: 100%;
padding: 20px 10px;
background-color: #f1de00;
box-shadow: 0 3px 6px rgba(0, 0, 0, .16);
color: #222222;
font-size: 18px;
line-height: 1.5;
text-align: center;
text-decoration: none;
transition: .25s;
}
HTML側の内容を見ると、それぞれのボタン(a
タグ)に割り当てられたクラス名はそれぞれ一つずつだ。
それに対し、CSS側ではほとんどの内容が重複してしまっているのが分かるだろうか。
異なるのは以下のプロパティだけだ。
background-color
color
…ということは、この二つ以外は共通設定としてグループセレクタ―で定義し、ここだけそれぞれで定義すればいいのでは?という修正案が一つある。
具体的には、以下のように設定する。
@charset "UTF-8";
/* グループセレクタ―で共通の内容を定義 */
.el_btnTheme,
.el_btnWarning {
display: inline-block;
width: 300px;
max-width: 100%;
padding: 20px 10px;
box-shadow: 0 3px 6px rgba(0, 0, 0, .16);
font-size: 18px;
line-height: 1.5;
text-align: center;
text-decoration: none;
transition: .25s;
}
/* 異なる内容はそれぞれで定義 */
.el_btnTheme {
background-color: #e25c00;
color: #ffffff;
}
.el_btnWarning {
background-color: #f1de00;
color: #222222;
}
確かに、現段階ではこれでもいいだろう。
しかし、今度は例えば…
- ボックスシャドーがないパターンが欲しい
- それぞれ文字色を反転させたパターンが欲しい
などなど、様々な修正が入る可能性だってある。
これをやろうとすると、最低でも今グループとして分けた部分のセレクターにクラスは追加しなければいけないし、さらに細分化する必要も出てくる可能性だって大いにあるだろう。
なぜこうなるかというと、シングルクラスではほんの一部しか違わないような部品を作る際にでも、新たなモジュールとして専用の新規クラスを作らなければいけないのだ。
じゃあどうするか、それを解決するのがマルチクラス設計になる。
マルチクラス設計
これも名前の通りで、マルチクラス設計においては一つのタグに複数のクラス名をつけることができる。
今度は、以下のように組んでみよう。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>第4回CSS設計サンプルページ</title>
<!-- css-wipeのファイル読み込み -->
<link rel="stylesheet" type="text/css" href="./css/css-wipe.css">
<!-- 自作CSSファイル読み込み -->
<link rel="stylesheet" type="text/css" href="./css/style.css">
</head>
<body>
<a class="el_btn hp_theme" href="#">ベースモジュール</a>
<a class="el_btn hp_warning" href="#">変更モジュール</a>
</body>
</html>
@charset "UTF-8";
.el_btn {
display: inline-block;
width: 300px;
max-width: 100%;
padding: 20px 10px;
box-shadow: 0 3px 6px rgba(0, 0, 0, .16);
font-size: 18px;
line-height: 1.5;
text-align: center;
text-decoration: none;
transition: .25s;
}
.hp_theme {
background-color: #e25c00;
color: #ffffff;
}
.hp_warning {
background-color: #f1de00;
color: #222222;
}
今、HTMLのa
タグそれぞれに二つのクラスをつけるようにした。
共通の設定をel_btn
というクラスで行い、それぞれの差をhp_theme
とhp_warning
という二つのクラスで設定している。
こうすれば、シングルクラスで共通の内容をグループセレクタ―で設定しようとしていた内容を、新たなクラスとして切り出すことができる。
実際に見比べてみてもらえれば分かると思うが、今の設定中身はシングルクラス設計の方で共通設定を切り出したものと同じだ。
ところが、このままだと例えば先ほども挙げたような…
- ボックスシャドーがないパターンが欲しい
- それぞれ文字色を反転させたパターンが欲しい
といった要望があったとき、やはりその部分を切り出す必要がありそうだ。
そこで、もう一つ手を加えてみる。
今度は以下のように組んでみよう。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>第4回CSS設計サンプルページ</title>
<!-- css-wipeのファイル読み込み -->
<link rel="stylesheet" type="text/css" href="./css/css-wipe.css">
<!-- 自作CSSファイル読み込み -->
<link rel="stylesheet" type="text/css" href="./css/style.css">
</head>
<body>
<a class="el_btn" href="#">ベースモジュール</a>
<a class="el_btn hp_warning" href="#">変更モジュール</a>
</body>
</html>
@charset "UTF-8";
.el_btn {
display: inline-block;
width: 300px;
max-width: 100%;
padding: 20px 10px;
background-color: #e25c00;
box-shadow: 0 3px 6px rgba(0, 0, 0, .16);
color: #ffffff;
font-size: 18px;
line-height: 1.5;
text-align: center;
text-decoration: none;
transition: .25s;
}
.hp_warning {
background-color: #f1de00;
color: #222222;
}
先ほどは分けていたhp_theme
クラスを消し、そこで設定していた内容をベースのel_btn
クラスに含めた。
つまり、hp_theme
でやっていた内容を大元のモジュールと定義し、追加のhp_warning
クラスがあれば差分を上書きする、ということをやっている。
今、これらの詳細度は共にクラスセレクタ―一つなので一致しており、この時は後で書いたものが適用されたことを思い出そう。
これで、クラスやCSSのブロックを減らすこともできる。
そして、新たに別のメリットも生まれている。
hp_warning
というクラスでやっているのは、背景色と文字色の設定のみ。
ということは、今回やっているボタン以外の要素でも汎用的に使える。
実際、クラス名の接頭語としてつけているhp_
はヘルパークラスというものを表していて、これが汎用的に使えるクラスを意味しているのだ。
なお、ヘルパークラスには意図的に!important
をつける場合もあるのだが、今回はちょっと話が複雑になってしまうので省略している。
シングル・マルチのトレードオフ
さて、二つの方針を紹介したが、それぞれがトレードオフになっている部分がある。
まず、シングルクラス設計では確かにCSSが煩雑になりやすいが、代わりにHTMLで見ると常に一つの要素には一つのクラスなので、こちらはシンプルになる。
逆に、マルチクラス設計ではCSSがスッキリするが、HTMLでは一つの要素に複数のクラスがつくことになり、こちらが複雑になってくる。
つまり、HTMLとCSSの複雑性がトレードオフの関係になっており、シングルクラスはHTML重視、マルチクラスはCSS重視ということだ。
で、じゃあどっちがいいのさと思われるかもしれないが、実際にはマルチクラス設計が主流だ。
HTMLが複雑になるデメリット以上に、CSSがシンプルになるメリットが大きい。
実際の設計手法でも、明記の有無の差はあれど、基本的にマルチクラス設計を前提としている。
拡張しやすい
では、本題へ。
サンプルは前回の続きを使おう。
二つ並んでいるが、上が前回までの内容、下が今回の内容だ。
また、今回は既存の内容を修正するのではなく、新たに機能を追加することを想定する。
どうするかというと、これまでは左揃えだったものを右揃えにしただけのモジュールを作るとしよう。
厳密には、左にあった画像を右に移動し、それぞれのテキストを左揃えから右揃えにしてみる。
それに伴い、画像とテキスト間のmargin-right
の設定も必要だ。
で、これをするときに元の内容へ手を加えてはいけない。
これをしてしまうと、前回までの内容である上の内容まで変わってしまうからだ。
そこで、新たにモディファイアというものを作る。
このモディファイアは既存のクラス設定に何かを変更したり上書きしたりするときに使うクラス全般で、マルチクラスのところで出てきたヘルパークラスはその一つ。
命名規則は、PRECSSの内容を参考に、元のクラス名__モディファイア名
という形を使う。
分かりづらいが、アンダースコアは二つ入っている。
今回は左右反転(reverse)なので、モディファイア名はrev
としよう。
以上を元に、ソースを変更してみる。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>第3回CSS設計サンプルページ</title>
<!-- css-wipeのファイル読み込み -->
<link rel="stylesheet" type="text/css" href="./css/css-wipe.css">
<!-- 自作CSSファイル読み込み -->
<link rel="stylesheet" type="text/css" href="./css/style.css">
</head>
<body>
<article id="main" class="ly-cont">
<div class="bl_media">
<figure class="bl_media_imageWrapper">
<img src="./image/sample.jpg" alt="写真:サンプル">
</figure>
<div class="bl_media_body">
<h2 class="bl_media_title">記事タイトルテキスト</h2>
<p>記事本文テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト<br>
テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト
</p>
<h3 class="subTitle">サブタイトル</h3>
<span>
テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト
</span>
</div>
</div>
<div class="bl_media bl_media__rev">
<figure class="bl_media_imageWrapper bl_media_imageWrapper__rev">
<img src="./image/sample.jpg" alt="写真:サンプル">
</figure>
<div class="bl_media_body bl_media_body__rev">
<h2 class="bl_media_title">記事タイトルテキスト</h2>
<p>記事本文テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト<br>
テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト
</p>
<h3 class="subTitle">サブタイトル</h3>
<span>
テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト
</span>
</div>
</div>
</article>
</body>
</html>
@charset "UTF-8";
/* === ベース === */
body {
font-family: sans-serif;
}
/* === レイアウト === */
.ly-cont {
max-width: 1200px;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
}
/* === モジュール === */
.bl_media {
/* モジュールに対する指定 */
display: flex;
align-items: center;
font-size: 16px;
line-height: 1.5;
}
.bl_media_imageWrapper {
flex: 1 1 25%;
margin-right: 3.33333%;
}
.bl_media_imageWrapper img {
width: 100%;
vertical-align: top;
}
.bl_media_body {
flex: 1 1 68.33333%;
}
.bl_media_title {
margin-bottom: 10px;
font-size: 18px;
font-weight: bold;
}
.bl_media_body > span {
color: #555;
font-size: 14px;
}
.subTitle {
margin-top: 10px;
margin-bottom: 10px;
font-size: 16px;
border-bottom: 1px solid #555;
}
/* === revモディファイア === */
.bl_media__rev {
flex-direction: row-reverse;
text-align: right;
}
.bl_media_imageWrapper__rev {
margin-right: 0;
}
.bl_media_body__rev {
margin-right: 3.33333%;
}
今、適用すべき設定を行う部分それぞれにモディファイアとなるクラスを作り、それで設定を適用させている。
これで、新たにモディファイアを適用したことにより、その内容に従って左右反転させることができた。
…のだが、実はこれではあまりよろしくない。
ここで、前回ちらっと書いたもう一つの観点、拡張用で作ったクラスは、適切な粒度や影響範囲を保つというのが出てくる。
もしそれぞれの部品でモディファイアを作り、それを他でも使いたいのであれば上の形でいいのだが、今回はこの3つをセットで使うことを想定している。
このままだと、左右反転という一つの追加機能を行うために、3か所の修正が必要になる。
それだと、どれか設定を忘れただけで上手くいかなくなってしまう。
そのため、ここは大元のモジュールに対してのみモディファイアクラスをつけるよう変更しよう。
上の中でbl_media__rev
クラスのみを残し、内側の二つはこれを親とする小孫セレクターを使って適用するように修正する。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>第3回CSS設計サンプルページ</title>
<!-- css-wipeのファイル読み込み -->
<link rel="stylesheet" type="text/css" href="./css/css-wipe.css">
<!-- 自作CSSファイル読み込み -->
<link rel="stylesheet" type="text/css" href="./css/style.css">
</head>
<body>
<article id="main" class="ly-cont">
<div class="bl_media">
<figure class="bl_media_imageWrapper">
<img src="./image/sample.jpg" alt="写真:サンプル">
</figure>
<div class="bl_media_body">
<h2 class="bl_media_title">記事タイトルテキスト</h2>
<p>記事本文テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト<br>
テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト
</p>
<h3 class="subTitle">サブタイトル</h3>
<span>
テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト
</span>
</div>
</div>
<div class="bl_media bl_media__rev">
<figure class="bl_media_imageWrapper">
<img src="./image/sample.jpg" alt="写真:サンプル">
</figure>
<div class="bl_media_body">
<h2 class="bl_media_title">記事タイトルテキスト</h2>
<p>記事本文テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト<br>
テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト
</p>
<h3 class="subTitle">サブタイトル</h3>
<span>
テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト
</span>
</div>
</div>
</article>
</body>
</html>
@charset "UTF-8";
/* === ベース === */
body {
font-family: sans-serif;
}
/* === レイアウト === */
.ly-cont {
max-width: 1200px;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
}
/* === モジュール === */
.bl_media {
/* モジュールに対する指定 */
display: flex;
align-items: center;
font-size: 16px;
line-height: 1.5;
}
.bl_media_imageWrapper {
flex: 1 1 25%;
margin-right: 3.33333%;
}
.bl_media_imageWrapper img {
width: 100%;
vertical-align: top;
}
.bl_media_body {
flex: 1 1 68.33333%;
}
.bl_media_title {
margin-bottom: 10px;
font-size: 18px;
font-weight: bold;
}
.bl_media_body > span {
color: #555;
font-size: 14px;
}
.subTitle {
margin-top: 10px;
margin-bottom: 10px;
font-size: 16px;
border-bottom: 1px solid #555;
}
/* === revモディファイア === */
.bl_media__rev {
flex-direction: row-reverse;
text-align: right;
}
.bl_media__rev .bl_media_imageWrapper {
margin-right: 0;
}
.bl_media__rev .bl_media_body {
margin-right: 3.33333%;
}
これで、モジュール部分にモディファイア名をつけるだけで、全体が左右反転するようになった。
このように、モディファイアは各要素ではなく、一つの機能に対して一つのモディファイアを作成するというのがポイントだ。
さて、今はモジュール全体に影響する追加機能だった。
これが、例えば画像に枠線を付けたいとなったとき、同じようにモジュールにモディファイアクラスをつけることはあまり推奨されない。
その時はあくまで画像に対する追加機能なので、figure
要素のところに.bl_media_imageWrapper__border
のようなモディファイアクラスをつけるようにしよう。
このように、モディファイアについてもやはりその名前から影響範囲が想像できることがもう一つのポイントになる。
…と、まあ色々書いてきたが、このモディファイアを使う際には注意点もある。
何かというと、色々機能を追加しすぎると今度はどのモディファイアが何をするものかが分かりづらくなってしまうのだ。
そのため、例えばそのモジュールに紐づくモディファイアではなく一般的なヘルパークラスで代用できないかや、そもそもそのモディファイアを付けた状態を元のモジュールの定義としてできないかも検討してみることが大事だ。
おわりに
今回は、CSS設計のゴールを達成するための8つの考え方のラスト一つ、拡張しやすいというものを紹介した。
これで、8つのポイント全てを解説したことになる。
前回も書いた通りだが、まだまだ実感は湧いてこないと思う。
が、これらはまだ考え方で、実際の手法を通して少しずつ理解していってもらえれば大丈夫だ。
さて、次回は具体的な設計手法…にはまだ入らない。
ここまで一つのモジュールに対して修正を加えることを想定してサンプルを書いてきた。
…のだが、そもそもモジュールってどのくらいの範囲を指すのだろうか。
そんなことを、次回深掘りしてみる。
これが終わったら、ようやく具体的な手法に入れると思う。
そこまであと少し、基礎を固めていこう。
2021/3/14追記
次回の内容、モジュールとその粒度について解説した。
やはり、この段階ではまだそれを考える必要があるというのと、それ以上分けられるかどうかを考えるといい、という2つだけ把握しておけばOKだ。
その次から具体的な設計手法に入り、具体的な考え方も出てくると思うので、そこで感覚を養っていきたい。
コメント