広告のWebサイトを作っていると、メーカーサイトの下層ページとして商品紹介ページを作るというパターンがよくあります。
その場合、既存の共通ヘッダーや共通フッターがテンプレートとして提供されていて、受託した会社はそれ以外のメインのコンテンツを制作することが多いです。
ですが、そこで提供される共通のパーツに問題があって、下層ページをコーディングしていく上で悪い影響を受けることがあります。
共通パーツによる問題が起きると、原因を把握しにくく、無駄な挙動を無くすかあるいは競合しない対策を取らなければならないので、対応するのが手間です。そしてまた、それは無駄な手間です。
そういった問題が起こらないようにするために、どうすれば問題を最小限に抑えられるかを考えました。

まず、前提としてあるのが、携わる制作者のスキルや考え方がバラバラで、それは変えられないということです。
ひとつのチームとしてルールを決めて制作していくようなサイトと違って、思想も統一できません。
そのため、共通パーツと下層ページは互いに独立して存在できる状態にするべきです。いずれかが一方のCSSやJSに依存しないようにしてください。
そうすることで、お互いが安全にページを変更することができる状態を保つことができ、下層ページの制作者にも、それぞれのやり方を尊重して気分よく作業してもらうことができます。

共通CSSの設計

共通パーツの辛い部分として、共通CSSがあります。
CSS自体の癖のあるパーツとして、

  • 全てのセレクタがグローバル空間に存在している
  • スタイルがカスケーディングする

ということがあります。
これらの機能のために、CSSは複数人で書き進めていくのが非常に難しいという現状があります。

共通CSSでは、そのような問題を回避するために、以下の様な手法を取ります。

  • グローバルにCSSをリセットしない
  • 共通パーツの全てのクラス名に固有のプレフィックスを付与する
  • セレクタのルートとして要素名を使用しない

グローバルにCSSをリセットしない

まず、リセットCSSはスタイルのベースとなるので、以降のCSSを書いていくことにおいて非常に影響力の大きい部分です。制作者の求めていない形でリセットをされると、かなりやりにくくなってきます。
それを避けるために、スタイルのベースとしては、normalize.cssでブラウザによるスタイルの差異を吸収することのみに抑えます。リセットについては後にも言及します。

共通パーツの全てのクラス名に固有のプレフィックスを付与する

CSSのクラス名は、他とバッティングする可能性があります。
それを避けるために、共通パーツとして提供するCSSのクラス名の全てに固有のプレフィックスを付与します。

.common-header {
  /* 共通ヘッダー */
}

.common-footer {
  /* 共通フッター */
}

.common-breadcrumb {
  /* パンくずリスト */
}

そうした上で、共通パーツ以外のクラス名には同じプレフィックスのついたクラス名の使用を禁じます。このルールを守れば、擬似的にスコープを作り出せます。

セレクタのルートとして要素名を使用しない

a {}

img {}

p {}

以上のようなセレクタにスタイルを指定してはいけません。
ただし、以下の様なものは可能です。

.common-header a {
  color: red;
}

.common-header img {
  max-width: 100%;
}

これは、不要なカスケーディングを避けるため、また、CSSの早すぎる最適化を避けるためです。
CSSの早すぎる最適化とは、例えば、アンカー要素に対してボタンのようなスタイリングを行ったが、別のアンカー要素ではそのスタイリングが邪魔になり、もともとの値でリセットし直さなければならないというようなケースのことです。

a {
  display: inline-block;
  padding: 10px;
  color: white;
  text-decoration: none;
  background-color: blue;
  border: 1px solid black;
}

a.other-link {
  display: inline;
  padding: 0;
  color: blue;
  text-decoration: underline;
  background-color: transparent;
  border: none;
}

これは例外として、最初に読み込むnormalize.cssと、body に対する非継承プロパティを除きます。
例えば共通パーツの中でカスケーディングさせたいスタイルがある場合、またはスタイルをリセットしたいとき、SCSSの場合なら以下のようにします。

@import "normalize.css";

%root {
  box-sizing: border-box;
  font-family: "Hiragino Kaku Gothic ProN", sans-serif;
  font-size: 14px;
  line-height: 1;
  color: #222;

  * {
    margin: 0;
    padding: 0;
    box-sizing: inherit;
  }

  a {
    text-decoration: none;

    &:hover,
    &:focus {
      text-decoration: underline;
    }
  }

  img {
    max-width: 100%;
  }
}

%clearfix {
  &::after {
    content: "";
    display: block;
    clear: both;
  }
}

body {
  background-color: #eee;
}

.common-header {
  @extend %root;

  // do stuff
}

.common-footer {
  @extend %root;

  // do stuff
}

.common-breadcrumb {
  @extend %root;

  ul {
    @extend %clearfix;
  }

  li {
    float: left;
  }

  // do stuff
}

これらCSSの問題は、将来的にはScoped CSSなどの仕様によって解決されるはずですが、主要ブラウザに実装されるのはまだまだ先になると思います。

共通JavaScript

共通JavaScriptに求めることは、勝手に共通パーツ以外に影響を与えることをしないということです。
例えば、ハッシュリンクをクリックするとその要素の位置までスムーススクロールする、SNSにシェアするリンクをクリックすると window.openでリンクが開く、といったような処理が勝手に書かれていることがあります。
一見便利なように見えますが、その存在を知らされずにページを制作している立場からすると、ページが予期しない動作をすることに戸惑ったり、そのせいで知らず知らずバグを作っていることもあります。
また、下層ページの実装が共通パーツの処理に依存していると、共通パーツを変更した際に、下層ページが知らず知らず壊れてしまうこともあります。
共通JavaScriptに書く処理は、共通パーツの挙動のみに留め、その他のことには影響を与えないようにしてください。

また、可能であればライブラリの使用を控えます。
ライブラリのバグや、バージョンアップなどによる挙動の違いに振り回されないために、生のJavaScriptで実装されることが理想です。
下層ページの制作者が、同じライブラリの別バージョンを使用していたため、同ページ内でバッティングしたり、または、共通パーツのライブラリに依存して実装をしたため、共通パーツのライブラリのバージョンを変更して処理が壊れたりといったような問題が起こる可能性があります。
ライブラリの使用を控えることで、悪影響を防ぎ、そのパーツの寿命を伸ばすことができます。

そして当然ですが、無駄なグローバル変数を作らないでください。

まとめ

共通パーツは謙虚に振る舞ってください。