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>

Screen Shot 2021-03-09 at 15.48.18

※ 分かりやすくするために 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>

Screen Shot 2021-03-09 at 16.03.26

別のレイアウト下に 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>

Screen Shot 2021-03-09 at 16.07.14

このように .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>

Screen Shot 2021-03-09 at 16.26.35

このように .logo にはデザインに関わるプロパティしか使用していません。これにより例え、 .header__logo とは違う場所に使用されたとしても正常に表示させることができます。横幅などのサイズの調整は、 .header__logo など .logo の親のレイアウトに関するCSSにのみに任せることで、ありとあらゆる状況に流用できる強固なCSSができあがります。

コンポーネント指向設計に繋がる

レイアウトとデザインを分けるということは、ゆくゆくはコンポーネント指向設計に繋がります。

一般なCSS学習書やスクールを使用して学ぶと、デザインを起こすことについては学ぶことはできるのですが、どのようなアプローチでCSSを設計すればよいのかということは書かれていません。

CSSは非常に柔軟でありつつも、脆弱な言語です。人間がしっかりと設計して組まないと、容易に互いが互いを破壊しあうコードが完成してしまいます。

CSS設計は頭のいい人が沢山考えている

なにもCSS設計は難しいことではありません。既にこの問題について多数の頭の良い人たちが解決のための方法を編み出しています。

一般的にBEMが一番使用されていることが多いかと思います。今回のサンプルもBEMを使用しています。

一個を覚えてしまえば、他の設計思想も同じようなものなのですぐ使うことができるようになります。

まとめ

CSS設計を上手に使用すれば、よりバグが発生しづらく、工数もかかりにくい強固なサイトができるようになります。無秩序で構築をすると、後でのメンテナンスは悲惨なことになります。

他にも、多数のやってはいけないバッドプラクティスがあります。

  • IDセレクタや、属性セレクタを使用しない
  • .header .logo {} など入れ子にしない

などなど、それはまたの機会に。

それではまたお会いしましょう

Share Button