辛くならないmarginの付け方を考える

WIP: 図は時間あるときに付けます。

なんでそんなマージンの付け方してるんだー。ってならないことがない。

CSSは簡単なのだけど、継続的に開発するのが非常に難しい。

maginの設計もアプリケーションで統一してないと、Viewが動的に変化する箇所で、

意図せず、隙間が空きすぎたり、隙間が狭すぎたりしちゃうことがある。

簡単にmarginを運用するために、僕は以下の方針で設計しています。

垂直方向のmarginは基本的に下にしか付けない

部品によって、上下にmarginが付いてたり、上についてたり、下についてたりすることがある。

marginが統一されてないと、部品を使い回す場合や、動的にViewが変更される場合に問題になることが多い。

// 図

上か下どっちかに統一されていればいいのだけど、 個人的には下についていた方がわかりやすい。

コンテナ要素内の最後の部品のmargin-bottomは消す

連続要素や、動的にViewが追加・削除される場合、最後のmargin-bottomが問題になることがある。

// 図

コンテナ要素のpaddingをmargin-bottomで作ってしまうコードもよく見るけど、 そうすると、後からその下に要素を追加する必要が出てきた時に問題になりやすい

// 図

なので、僕の場合はコンテナ要素で > *:last-child に対して margin-bottom: 0 として最後の部品のマージンを消すようにする。

.is-packed > *:last-child {
  margin-bottom: 0;
}

.item {
  margin: 0;
  margin-bottom: 8px;
}
<div class="is-packed">
  <p class="item">item1</p>
  <p class="item">item2</p>
</div>

デフォルトマージンは付けても良いかなー

部品を使い回すときに、マージンが付いていると使いにくい場合があるので、 部品自体にmargin-bottomを定義しない方法もあると思う。 p要素などの標準のmarginを消してしまうリセットCSSもあるぐらいなので。

/* topSearchBarContainerでsearchBarをラップしてトップページで使うみたいな感じ */

.topSearchBarContainer {
  marginBottom: 8px;
}

.searchBar {
  backgroundColor: '#fff';
  border: 1px solid black;
  borderRadius: 3px;
  padding: 4px 6px;
}

部品とそれを配置(レイアウト)してページを作る部分のスタイルを完全に分けてやる場合は、うまくいくのだけど、 結構堅めの設計になるので、僕の場合は結構デフォルトマージンつけちゃうかも。

多くの部品は、他の画面で使う場合も同じマージンをあける事があるので、デフォルトでmargin-bottomを定義しておき、 特定のページだけ、marginを調整したい場合はスタイルを上書きする形で定義して、classに追加する。 単純にmargin-bottomを消したい場合は、汎用的なmargin-bottomを0にするスタイルを定義しておけばいい。

コンテナと部品によってマークアップする

部品の集まりをコンテナでラップしてpaddingや、margin-bottomを付けて隙間を作るようにすると結構うまくいく。 特定の部品の集まりの後に、次の要素を配置する場合のmargin-bottomは、前の要素の部品よりも大きめの隙間を空けたいことが多い。

// 図

次のまとまりの間の隙間を作るために、コンテナ内の最後の要素でマージンを作るコードをよく見る。 sectionとかは集合がわかりやすいので、sectionで説明すると、コンテナで部品集合間のmargin-bottomを管理して、 is-packedすることで、コンテナ内の最後のmargin-bottomを削除してpaddingでコンテナ内の隙間を管理しやすくする。

.profile-section {
  padding: 8px;
  margin-bottom: 12px;
}

/* h1/p要素のmargin-topは0としている前提 */
<section class="profile-section is-packed">
  <h1>Your name.</h1>
  <p>hikouki</p>
</section>
<section class="profile-section is-packed">
  <h1>Your job</h1>
  <p>Programmer</p>
</section>

// 図

sectionで説明しましたが、普通は、section内にもコンテナがあって、コンテナ間で隙間を作り合うことは結構ある。 部品集合をコンテナでラップして隙間を管理すると、コンテナに新しい部品を追加した時でも、コンテナ内のpaddingやコンテナ間のmarginが変わらないので保守性が高まる。

コンテナ要素でネガティブマージンを使う

flexで、水平方向に連続要素を並べたいことは良くあると思う。

1列の場合はそんなに問題にならないが、2列以上に flex-wrap: wrap する場合は上下のマージンで問題になることがある。

// 図

連続要素自体にmargin-bottomを付けてしまうとコンテナのpaddingにmargin-bottom分が加算されて管理が難しくなる。 なので、親の要素でネガティブマージンを付けて連続要素の隙間を相殺する形で定義すると良い。

.container {
  /* overflowしないと画面端からはみ出て水平方向にスクロールバーが出たりする */
  overflow: hidden;
  margin-bottom: 12px;
}

.row {
  margin: -4px -8px;
}

.col {
  padding: 4px 8px; 
}
<div class="container">
  <div class="row">
    <div class="col">
      <article class="news-card">
        <h1>Today's Weather</h1>
         .....
      </article>
    </div>
  </div>
</div>