CSSでレイアウトとデザインを分けることの解説
2021年3月8日
皆さんこんにちは、末ちゃんです。
今日は、よくCSSの初学者に言われる「レイアウトとデザインは分けた方が良い」ということの解説をしたいと思います。
レイアウトとデザインのclassを分ける
まず最初に行うべきアプローチは、レイアウトとデザインのためのスタイルはclassを分けると言うことです。
まずはレイアウトを組む
<div class="layout"> <div class="layout__header"></div> <div class="layout__main"></div> <div class="layout__footer"></div> </div> <style> body { margin: 0; } .layout { display: flex; flex-direction: column; min-height: 100vh; } .layout__header { min-height: 60px; background-color: lightpink; } .layout__main { flex-grow: 1; background-color: lightblue; } .layout__footer { min-height: 100px; margin-top: auto; background-color: lightsalmon; } </style>
※ 分かりやすくするために background-color
を付与していますが、実際の運用では推奨しません。
ヘッダーにロゴと、ナビを付与する
一般的なCSS学習書やスクールでは、恐らく以下のようなコードになると思います。
<div class="layout"> <div class="layout__header"> <div class="logo"></div> <div class="nav"></div> </div> <div class="layout__main"></div> <div class="layout__footer"></div> </div> <style> body { margin: 0; } .layout { display: flex; flex-direction: column; min-height: 100vh; } .layout__header { min-height: 60px; background-color: lightpink; /* 追加ここから */ display: flex; } .logo { flex-basis: 100px; background-color: teal; } .nav { flex-basis: 300px; margin-left: auto; background-color: yellow; } /* 追加ここまで */ .layout__main { flex-grow: 1; background-color: lightblue; } .layout__footer { min-height: 100px; margin-top: auto; background-color: lightsalmon; } </style>
別のレイアウト下に logo と nav をコピーする
しかし、この実装には重大な問題があります。このヘッダーを再利用したい場合、親の .layout
ごと持っていかないとデザインが成り立たないということです。
<div class="sample"> <div class="sample__header"> <div class="logo"></div> <div class="nav"></div> </div> <div class="sample__main"></div> <div class="sample__footer"></div> </div> <style> body { margin: 0; } .sample { display: grid; grid-template-rows: 60px 1fr 100px; min-height: 100vh; } .sample__header { background-color: lightpink; } .sample__main { background-color: lightblue; } .sample__footer { background-color: lightsalmon; } .logo { flex-basis: 100px; background-color: teal; min-height: 60px; } .nav { flex-basis: 300px; margin-left: auto; background-color: yellow; min-height: 60px; } </style>
このように .logo
と .nav
は親の .layout__header
に依存していたため、同じようにレンダリングされませんでした。
logo と nav をカプセル化する
こうならないようにするために .logo
と .nav
を何らかのクラスで囲います。今回は .header
で囲い .logo
と .nav
も .header__
のプリフィックスをつけます。
<div class="layout"> <div class="layout__header"> <div class="header"> <div class="header__logo"></div> <div class="header__nav"></div> </div> </div> <div class="layout__main"></div> <div class="layout__footer"></div> </div> <style> body { margin: 0; } .layout { display: flex; flex-direction: column; min-height: 100vh; } .layout__header { min-height: 60px; background-color: lightpink; } .layout__main { flex-grow: 1; background-color: lightblue; } .layout__footer { min-height: 100px; margin-top: auto; background-color: lightsalmon; } /* 追加ここから */ .header { display: flex; } .header__logo { flex-basis: 100px; background-color: teal; min-height: 60px; } .header__nav { flex-basis: 300px; margin-left: auto; background-color: yellow; min-height: 60px; } /* 追加ここまで */ </style>
これで .header
をまるごと持っていけばどこでも正常に表示されるようになりました。
<div class="sample"> <div class="sample__header"> <div class="header"> <div class="header__logo"></div> <div class="header__nav"></div> </div> </div> <div class="sample__main"></div> <div class="sample__footer"></div> </div> <style> body { margin: 0; } .sample { display: grid; grid-template-rows: 60px 1fr 100px; min-height: 100vh; } .sample__header { background-color: lightpink; } .sample__main { background-color: lightblue; } .sample__footer { background-color: lightsalmon; } /* 追加ここから */ .header { display: flex; } .header__logo { flex-basis: 100px; background-color: teal; min-height: 60px; } .header__nav { flex-basis: 300px; margin-left: auto; background-color: yellow; min-height: 60px; } /* 追加ここまで */ </style>
.sample
には一切コードの修正が加わっていないことが分かると思います。
logo の装飾をする
さてここまでは装飾を行うCSSは使用していません。使用しているのは以下のレイアウトに関するプロパティのみです。
- display
- margin
- width (flex-basis)
- height
逆にスタイルを調整するCSSを書くときは、上記のプロパティは使用しません。ただしデザインの都合上 display: inline-block
などを使用することはあります。
<div class="layout"> <div class="layout__header"> <div class="header"> <div class="header__logo"> <div class="logo">Logo</div> </div> <div class="header__nav"></div> </div> </div> <div class="layout__main"></div> <div class="layout__footer"></div> </div> <style> body { margin: 0; } .layout { display: flex; flex-direction: column; min-height: 100vh; } .layout__header { min-height: 60px; background-color: lightpink; } .layout__main { flex-grow: 1; background-color: lightblue; } .layout__footer { min-height: 100px; margin-top: auto; background-color: lightsalmon; } .header { display: flex; } .header__logo { flex-basis: 100px; background-color: teal; min-height: 60px; } .header__nav { flex-basis: 300px; margin-left: auto; background-color: yellow; min-height: 60px; } /* 追加ここから */ .logo { border: 2px solid white; text-align: center; text-transform: uppercase; font-size: 14px; color: white; background-color: blue; min-height: 60px; } /* 追加ここまで */ </style>
このように .logo
にはデザインに関わるプロパティしか使用していません。これにより例え、 .header__logo
とは違う場所に使用されたとしても正常に表示させることができます。横幅などのサイズの調整は、 .header__logo
など .logo
の親のレイアウトに関するCSSにのみに任せることで、ありとあらゆる状況に流用できる強固なCSSができあがります。
コンポーネント指向設計に繋がる
レイアウトとデザインを分けるということは、ゆくゆくはコンポーネント指向設計に繋がります。
一般なCSS学習書やスクールを使用して学ぶと、デザインを起こすことについては学ぶことはできるのですが、どのようなアプローチでCSSを設計すればよいのかということは書かれていません。
CSSは非常に柔軟でありつつも、脆弱な言語です。人間がしっかりと設計して組まないと、容易に互いが互いを破壊しあうコードが完成してしまいます。
CSS設計は頭のいい人が沢山考えている
なにもCSS設計は難しいことではありません。既にこの問題について多数の頭の良い人たちが解決のための方法を編み出しています。
一般的にBEMが一番使用されていることが多いかと思います。今回のサンプルもBEMを使用しています。
一個を覚えてしまえば、他の設計思想も同じようなものなのですぐ使うことができるようになります。
まとめ
CSS設計を上手に使用すれば、よりバグが発生しづらく、工数もかかりにくい強固なサイトができるようになります。無秩序で構築をすると、後でのメンテナンスは悲惨なことになります。
他にも、多数のやってはいけないバッドプラクティスがあります。
- IDセレクタや、属性セレクタを使用しない
- .header .logo {} など入れ子にしない
などなど、それはまたの機会に。
それではまたお会いしましょう